mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-25 02:57:22 +00:00
commit
54d8240dc0
@ -20,11 +20,11 @@ def init_cache_busting(app):
|
||||
|
||||
app.logger.debug('Computing cache-busting values...')
|
||||
# compute file hashes
|
||||
for dirpath, dirnames, filenames in os.walk(static_folder):
|
||||
for dirpath, __, filenames in os.walk(static_folder):
|
||||
for filename in filenames:
|
||||
# compute version component
|
||||
rooted_filename = os.path.join(dirpath, filename)
|
||||
with open(rooted_filename, 'r') as f:
|
||||
with open(rooted_filename, 'rb') as f:
|
||||
file_hash = hashlib.md5(f.read()).hexdigest()[:7]
|
||||
|
||||
# save version to tables
|
||||
|
@ -73,26 +73,27 @@ if not os.path.exists(dbpath):
|
||||
migrate()
|
||||
|
||||
|
||||
def getDrive(gauth=None):
|
||||
if not gauth:
|
||||
gauth = GoogleAuth(settings_file='settings.yaml')
|
||||
# Try to load saved client credentials
|
||||
gauth.LoadCredentialsFile("gdrive_credentials")
|
||||
if gauth.access_token_expired:
|
||||
# Refresh them if expired
|
||||
gauth.Refresh()
|
||||
else:
|
||||
# Initialize the saved creds
|
||||
gauth.Authorize()
|
||||
# Save the current credentials to a file
|
||||
return GoogleDrive(gauth)
|
||||
def getDrive(drive=None, gauth=None):
|
||||
if not drive:
|
||||
if not gauth:
|
||||
gauth = GoogleAuth(settings_file='settings.yaml')
|
||||
# Try to load saved client credentials
|
||||
gauth.LoadCredentialsFile("gdrive_credentials")
|
||||
if gauth.access_token_expired:
|
||||
# Refresh them if expired
|
||||
gauth.Refresh()
|
||||
else:
|
||||
# Initialize the saved creds
|
||||
gauth.Authorize()
|
||||
# Save the current credentials to a file
|
||||
return GoogleDrive(gauth)
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
return drive
|
||||
|
||||
|
||||
def getEbooksFolder(drive=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
ebooksFolder = "title = '%s' and 'root' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % config.config_google_drive_folder
|
||||
|
||||
fileList = drive.ListFile({'q': ebooksFolder}).GetList()
|
||||
@ -113,20 +114,14 @@ def getEbooksFolderId(drive=None):
|
||||
|
||||
|
||||
def getFolderInFolder(parentId, folderName, drive=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
folder = "title = '%s' and '%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % (folderName.replace("'", "\\'"), parentId)
|
||||
fileList = drive.ListFile({'q': folder}).GetList()
|
||||
return fileList[0]
|
||||
|
||||
|
||||
def getFile(pathId, fileName, drive=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'"))
|
||||
|
||||
fileList = drive.ListFile({'q': metaDataFile}).GetList()
|
||||
@ -134,10 +129,7 @@ def getFile(pathId, fileName, drive=None):
|
||||
|
||||
|
||||
def getFolderId(path, drive=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
currentFolderId = getEbooksFolderId(drive)
|
||||
sqlCheckPath = path if path[-1] == '/' else path + '/'
|
||||
storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first()
|
||||
@ -168,10 +160,7 @@ def getFolderId(path, drive=None):
|
||||
|
||||
|
||||
def getFileFromEbooksFolder(drive, path, fileName):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
if path:
|
||||
# sqlCheckPath=path if path[-1] =='/' else path + '/'
|
||||
folderId = getFolderId(path, drive)
|
||||
@ -182,10 +171,7 @@ def getFileFromEbooksFolder(drive, path, fileName):
|
||||
|
||||
|
||||
def copyDriveFileRemote(drive, origin_file_id, copy_title):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
copied_file = {'title': copy_title}
|
||||
try:
|
||||
file_data = drive.auth.service.files().copy(
|
||||
@ -197,19 +183,13 @@ def copyDriveFileRemote(drive, origin_file_id, copy_title):
|
||||
|
||||
|
||||
def downloadFile(drive, path, filename, output):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
f = getFileFromEbooksFolder(drive, path, filename)
|
||||
f.GetContentFile(output)
|
||||
|
||||
|
||||
def backupCalibreDbAndOptionalDownload(drive, f=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
metaDataFile = "'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId()
|
||||
|
||||
fileList = drive.ListFile({'q': metaDataFile}).GetList()
|
||||
@ -221,12 +201,10 @@ def backupCalibreDbAndOptionalDownload(drive, f=None):
|
||||
|
||||
|
||||
def copyToDrive(drive, uploadFile, createRoot, replaceFiles,
|
||||
ignoreFiles=[],
|
||||
ignoreFiles=None,
|
||||
parent=None, prevDir=''):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
ignoreFiles = ignoreFiles or []
|
||||
drive = getDrive(drive)
|
||||
isInitial = not bool(parent)
|
||||
if not parent:
|
||||
parent = getEbooksFolder(drive)
|
||||
@ -254,10 +232,7 @@ def copyToDrive(drive, uploadFile, createRoot, replaceFiles,
|
||||
|
||||
|
||||
def uploadFileToEbooksFolder(drive, destFile, f):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
parent = getEbooksFolder(drive)
|
||||
splitDir = destFile.split('/')
|
||||
for i, x in enumerate(splitDir):
|
||||
@ -281,10 +256,7 @@ def uploadFileToEbooksFolder(drive, destFile, f):
|
||||
|
||||
def watchChange(drive, channel_id, channel_type, channel_address,
|
||||
channel_token=None, expiration=None):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
# Watch for all changes to a user's Drive.
|
||||
# Args:
|
||||
# service: Drive API service instance.
|
||||
@ -327,10 +299,7 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address,
|
||||
Raises:
|
||||
apiclient.errors.HttpError: if http request to create channel fails.
|
||||
"""
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
|
||||
body = {
|
||||
'id': channel_id,
|
||||
@ -353,10 +322,7 @@ def stopChannel(drive, channel_id, resource_id):
|
||||
Raises:
|
||||
apiclient.errors.HttpError: if http request to create channel fails.
|
||||
"""
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
# service=drive.auth.service
|
||||
body = {
|
||||
'id': channel_id,
|
||||
@ -366,10 +332,7 @@ def stopChannel(drive, channel_id, resource_id):
|
||||
|
||||
|
||||
def getChangeById (drive, change_id):
|
||||
if not drive:
|
||||
drive = getDrive()
|
||||
if drive.auth.access_token_expired:
|
||||
drive.auth.Refresh()
|
||||
drive = getDrive(drive)
|
||||
# Print a single Change resource information.
|
||||
#
|
||||
# Args:
|
||||
|
@ -266,6 +266,7 @@ def get_valid_filename(value, replace_whitespace=True):
|
||||
"""
|
||||
if value[-1:] == u'.':
|
||||
value = value[:-1]+u'_'
|
||||
value = value.replace("/", "_").replace(":", "_").strip('\0')
|
||||
if use_unidecode:
|
||||
value=(unidecode.unidecode(value)).strip()
|
||||
else:
|
||||
|
105
cps/static/css/kthoom.css
Normal file
105
cps/static/css/kthoom.css
Normal file
@ -0,0 +1,105 @@
|
||||
body {
|
||||
background: #444;
|
||||
overflow: hidden;
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.main {
|
||||
position: re;
|
||||
left: 5px;
|
||||
overflow: hidden;
|
||||
right: 5px;
|
||||
text-align: center;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
#progress {
|
||||
position: absolute;
|
||||
display: inline;
|
||||
left: 90px;
|
||||
right: 160px;
|
||||
height: 20px;
|
||||
margin-top: 1px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#mainText {
|
||||
text-align: left;
|
||||
width: 90%;
|
||||
position: relative;
|
||||
top: 10px;
|
||||
background: #ccc;
|
||||
color: black;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding: 10px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
#mainImage{
|
||||
margin-top: 32px;
|
||||
}
|
||||
|
||||
#titlebar.main {
|
||||
opacity: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 30px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: black;
|
||||
padding-bottom: 70px;
|
||||
-webkit-transition: opacity 0.2s ease;
|
||||
-moz-transition: opacity 0.2s ease;
|
||||
transition: opacity 0.2s ease;
|
||||
background: -moz-linear-gradient(top, rgba(0,2,34,1) 0%, rgba(0,1,24,1) 30%, rgba(0,0,0,0) 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(0,2,34,1)), color-stop(30%,rgba(0,1,24,1)), color-stop(100%,rgba(0,0,0,0))); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#000222', endColorstr='#00000000',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, rgba(0,2,34,1) 0%,rgba(0,1,24,1) 30%,rgba(0,0,0,0) 100%); /* W3C */
|
||||
}
|
||||
|
||||
#prev {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
#next {
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -32px;
|
||||
font-size: 64px;
|
||||
color: #E2E2E2;
|
||||
font-family: arial, sans-serif;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.arrow:hover {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.arrow:active,
|
||||
.arrow.active {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -90,3 +90,9 @@ input.pill:not(:checked) + label .glyphicon {
|
||||
#meta-info img { max-height: 150px; max-width: 100px; cursor: pointer; }
|
||||
|
||||
.padded-bottom { margin-bottom: 15px; }
|
||||
|
||||
.upload-format-input-text {display: initial;}
|
||||
#btn-upload-format {display: none;}
|
||||
|
||||
.upload-format-input-text {display: initial;}
|
||||
#btn-upload-format {display: none;}
|
||||
|
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="673.826" height="144" viewBox="0 0 673.826 144"><g fill="#5A481C"><path d="M66.66 86.444h-.315c-3.34 14.507-18.19 22.964-32.213 22.964C11.33 109.408 0 91.212 0 70.163c0-22.008 12.146-40.18 35.245-40.18 15.643 0 27.917 10.368 31.1 23.76h.315v-21.85h3.194v79.26C69.854 133.474 57.09 144 35.71 144c-16.576 0-30.78-7.49-31.257-25.843h3.205c.642 16.273 13.08 22.65 27.896 22.65 19.787 0 31.106-9.402 31.106-29.656V86.445zM35.245 33.176c-21.215 0-32.062 17.06-32.062 36.987 0 20.25 10.846 36.062 30.768 36.062 21.065 0 32.558-16.295 32.558-36.062.152-18.825-10.678-36.987-31.263-36.987zM115.787 29.982c23.926 0 36.825 20.58 36.825 42.897 0 22.482-12.898 42.894-36.987 42.894-23.915 0-36.853-20.41-36.853-42.895 0-22.318 12.938-42.898 37.015-42.898zm0 82.598c21.828 0 33.642-18.972 33.642-39.7 0-20.418-11.815-39.704-33.643-39.704-22.176 0-33.81 19.287-33.81 39.703 0 20.728 11.634 39.7 33.81 39.7zM194.57 29.982c23.908 0 36.824 20.58 36.824 42.897 0 22.482-12.916 42.894-36.987 42.894-23.925 0-36.84-20.41-36.84-42.895-.002-22.318 12.914-42.898 37.003-42.898zm0 82.598c21.856 0 33.643-18.972 33.643-39.7 0-20.418-11.786-39.704-33.643-39.704-22.17 0-33.822 19.287-33.822 39.703 0 20.728 11.65 39.7 33.822 39.7zM304.436 0h3.194v113.86h-3.194V90.91h-.326c-4.14 14.337-16.082 24.863-32.837 24.863-21.7 0-34.942-18.027-34.942-42.73 0-22.97 12.3-43.06 34.943-43.06 17.386 0 29.02 10.053 32.837 24.87h.326V0zm-33.163 33.176c-22.493 0-31.736 20.883-31.736 39.866 0 21.04 10.526 39.538 31.736 39.538 21.052 0 33.163-18.32 33.163-39.538 0-25.36-13.236-39.866-33.163-39.866zM323.093 31.58h9.25v19.286h.327c5.103-13.248 16.253-21.052 31.098-20.4v10.042c-18.196-.967-30.62 12.427-30.62 29.492v43.86h-10.054V31.58zM372.38 75.426c.147 14.684 7.806 32.363 27.092 32.363 14.688 0 22.65-8.604 25.832-21.03h10.064c-4.308 18.656-15.16 29.486-35.896 29.486-26.124 0-37.146-20.097-37.146-43.52 0-21.693 11.02-43.543 37.146-43.543 26.483 0 37.032 23.132 36.21 46.243h-63.3zm53.25-8.446c-.462-15.148-9.886-29.363-26.158-29.363-16.42 0-25.495 14.372-27.09 29.363h53.248zM444.297 56.775c.945-19.293 14.508-27.592 33.333-27.592 14.54 0 30.342 4.47 30.342 26.46v43.71c0 3.836 1.923 6.063 5.915 6.063 1.113 0 2.36-.326 3.183-.64v8.445c-2.238.484-3.835.642-6.557.642-10.2 0-11.82-5.735-11.82-14.36h-.28c-7.04 10.683-14.226 16.745-30.05 16.745-15.124 0-27.573-7.467-27.573-24.078 0-23.12 22.48-23.913 44.185-26.46 8.31-.978 12.933-2.09 12.933-11.172 0-13.557-9.728-16.92-21.56-16.92-12.436 0-21.67 5.76-22.03 19.16H444.3zm53.61 12.106h-.314c-1.27 2.397-5.758 3.207-8.457 3.68-17.082 3.024-38.292 2.867-38.292 18.968 0 10.054 8.93 16.262 18.342 16.262 15.317 0 28.89-9.716 28.722-25.82V68.88zM596.488 113.86h-9.232V98.24h-.326c-4.308 10.685-17.386 18.006-29.34 18.006-25.068 0-37.01-20.23-37.01-43.52 0-23.29 11.94-43.543 37.01-43.543 12.27 0 24.223 6.22 28.52 18.016h.348V0h10.03v113.86zm-38.9-6.07c21.356 0 28.868-18.017 28.868-35.063 0-17.083-7.512-35.11-28.868-35.11-19.14 0-26.956 18.027-26.956 35.11 0 17.046 7.817 35.062 26.956 35.062zM660.926 55.645c-.494-12.438-10.043-18.027-21.535-18.027-8.94 0-19.443 3.52-19.443 14.215 0 8.918 10.188 12.112 17.06 13.877l13.395 3.014c11.47 1.76 23.425 8.457 23.425 22.804 0 17.88-17.667 24.72-33.007 24.72-19.14 0-32.208-8.93-33.805-29.016h10.03c.82 13.54 10.864 20.558 24.27 20.558 9.38 0 22.47-4.14 22.47-15.62 0-9.56-8.92-12.745-18.005-14.99l-12.933-2.855c-13.068-3.51-22.976-7.984-22.976-22.008 0-16.745 16.43-23.132 30.95-23.132 16.418 0 29.52 8.625 30.14 26.46h-10.034z"/></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 673.826 144"><g fill="#5A481C"><path d="M66.66 86.444h-.315c-3.34 14.507-18.19 22.964-32.213 22.964C11.33 109.408 0 91.212 0 70.163c0-22.008 12.146-40.18 35.245-40.18 15.643 0 27.917 10.368 31.1 23.76h.315v-21.85h3.194v79.26C69.854 133.474 57.09 144 35.71 144c-16.576 0-30.78-7.49-31.257-25.843h3.205c.642 16.273 13.08 22.65 27.896 22.65 19.787 0 31.106-9.402 31.106-29.656V86.445zM35.245 33.176c-21.215 0-32.062 17.06-32.062 36.987 0 20.25 10.846 36.062 30.768 36.062 21.065 0 32.558-16.295 32.558-36.062.152-18.825-10.678-36.987-31.263-36.987zM115.787 29.982c23.926 0 36.825 20.58 36.825 42.897 0 22.482-12.898 42.894-36.987 42.894-23.915 0-36.853-20.41-36.853-42.895 0-22.318 12.938-42.898 37.015-42.898zm0 82.598c21.828 0 33.642-18.972 33.642-39.7 0-20.418-11.815-39.704-33.643-39.704-22.176 0-33.81 19.287-33.81 39.703 0 20.728 11.634 39.7 33.81 39.7zM194.57 29.982c23.908 0 36.824 20.58 36.824 42.897 0 22.482-12.916 42.894-36.987 42.894-23.925 0-36.84-20.41-36.84-42.895-.002-22.318 12.914-42.898 37.003-42.898zm0 82.598c21.856 0 33.643-18.972 33.643-39.7 0-20.418-11.786-39.704-33.643-39.704-22.17 0-33.822 19.287-33.822 39.703 0 20.728 11.65 39.7 33.822 39.7zM304.436 0h3.194v113.86h-3.194V90.91h-.326c-4.14 14.337-16.082 24.863-32.837 24.863-21.7 0-34.942-18.027-34.942-42.73 0-22.97 12.3-43.06 34.943-43.06 17.386 0 29.02 10.053 32.837 24.87h.326V0zm-33.163 33.176c-22.493 0-31.736 20.883-31.736 39.866 0 21.04 10.526 39.538 31.736 39.538 21.052 0 33.163-18.32 33.163-39.538 0-25.36-13.236-39.866-33.163-39.866zM323.093 31.58h9.25v19.286h.327c5.103-13.248 16.253-21.052 31.098-20.4v10.042c-18.196-.967-30.62 12.427-30.62 29.492v43.86h-10.054V31.58zM372.38 75.426c.147 14.684 7.806 32.363 27.092 32.363 14.688 0 22.65-8.604 25.832-21.03h10.064c-4.308 18.656-15.16 29.486-35.896 29.486-26.124 0-37.146-20.097-37.146-43.52 0-21.693 11.02-43.543 37.146-43.543 26.483 0 37.032 23.132 36.21 46.243h-63.3zm53.25-8.446c-.462-15.148-9.886-29.363-26.158-29.363-16.42 0-25.495 14.372-27.09 29.363h53.248zM444.297 56.775c.945-19.293 14.508-27.592 33.333-27.592 14.54 0 30.342 4.47 30.342 26.46v43.71c0 3.836 1.923 6.063 5.915 6.063 1.113 0 2.36-.326 3.183-.64v8.445c-2.238.484-3.835.642-6.557.642-10.2 0-11.82-5.735-11.82-14.36h-.28c-7.04 10.683-14.226 16.745-30.05 16.745-15.124 0-27.573-7.467-27.573-24.078 0-23.12 22.48-23.913 44.185-26.46 8.31-.978 12.933-2.09 12.933-11.172 0-13.557-9.728-16.92-21.56-16.92-12.436 0-21.67 5.76-22.03 19.16H444.3zm53.61 12.106h-.314c-1.27 2.397-5.758 3.207-8.457 3.68-17.082 3.024-38.292 2.867-38.292 18.968 0 10.054 8.93 16.262 18.342 16.262 15.317 0 28.89-9.716 28.722-25.82V68.88zM596.488 113.86h-9.232V98.24h-.326c-4.308 10.685-17.386 18.006-29.34 18.006-25.068 0-37.01-20.23-37.01-43.52 0-23.29 11.94-43.543 37.01-43.543 12.27 0 24.223 6.22 28.52 18.016h.348V0h10.03v113.86zm-38.9-6.07c21.356 0 28.868-18.017 28.868-35.063 0-17.083-7.512-35.11-28.868-35.11-19.14 0-26.956 18.027-26.956 35.11 0 17.046 7.817 35.062 26.956 35.062zM660.926 55.645c-.494-12.438-10.043-18.027-21.535-18.027-8.94 0-19.443 3.52-19.443 14.215 0 8.918 10.188 12.112 17.06 13.877l13.395 3.014c11.47 1.76 23.425 8.457 23.425 22.804 0 17.88-17.667 24.72-33.007 24.72-19.14 0-32.208-8.93-33.805-29.016h10.03c.82 13.54 10.864 20.558 24.27 20.558 9.38 0 22.47-4.14 22.47-15.62 0-9.56-8.92-12.745-18.005-14.99l-12.933-2.855c-13.068-3.51-22.976-7.984-22.976-22.008 0-16.745 16.43-23.132 30.95-23.132 16.418 0 29.52 8.625 30.14 26.46h-10.034z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
364
cps/static/js/archive.js
Normal file
364
cps/static/js/archive.js
Normal file
@ -0,0 +1,364 @@
|
||||
/**
|
||||
* archive.js
|
||||
*
|
||||
* Provides base functionality for unarchiving.
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
*/
|
||||
|
||||
/* global bitjs */
|
||||
|
||||
var bitjs = bitjs || {};
|
||||
bitjs.archive = bitjs.archive || {};
|
||||
|
||||
(function() {
|
||||
|
||||
// ===========================================================================
|
||||
// Stolen from Closure because it's the best way to do Java-like inheritance.
|
||||
bitjs.base = function(me, optMethodName, varArgs) {
|
||||
var caller = arguments.callee.caller;
|
||||
if (caller.superClass_) {
|
||||
// This is a constructor. Call the superclass constructor.
|
||||
return caller.superClass_.constructor.apply(
|
||||
me, Array.prototype.slice.call(arguments, 1));
|
||||
}
|
||||
|
||||
var args = Array.prototype.slice.call(arguments, 2);
|
||||
var foundCaller = false;
|
||||
for (var ctor = me.constructor;
|
||||
ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) {
|
||||
if (ctor.prototype[optMethodName] === caller) {
|
||||
foundCaller = true;
|
||||
} else if (foundCaller) {
|
||||
return ctor.prototype[optMethodName].apply(me, args);
|
||||
}
|
||||
}
|
||||
|
||||
// If we did not find the caller in the prototype chain,
|
||||
// then one of two things happened:
|
||||
// 1) The caller is an instance method.
|
||||
// 2) This method was not called by the right caller.
|
||||
if (me[optMethodName] === caller) {
|
||||
return me.constructor.prototype[optMethodName].apply(me, args);
|
||||
} else {
|
||||
throw Error(
|
||||
"goog.base called from a method of one name " +
|
||||
"to a method of a different name");
|
||||
}
|
||||
};
|
||||
bitjs.inherits = function(childCtor, parentCtor) {
|
||||
/** @constructor */
|
||||
function TempCtor() {}
|
||||
TempCtor.prototype = parentCtor.prototype;
|
||||
childCtor.superClass_ = parentCtor.prototype;
|
||||
childCtor.prototype = new TempCtor();
|
||||
childCtor.prototype.constructor = childCtor;
|
||||
};
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* An unarchive event.
|
||||
*
|
||||
* @param {string} type The event type.
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.archive.UnarchiveEvent = function(type) {
|
||||
/**
|
||||
* The event type.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.type = type;
|
||||
};
|
||||
|
||||
/**
|
||||
* The UnarchiveEvent types.
|
||||
*/
|
||||
bitjs.archive.UnarchiveEvent.Type = {
|
||||
START: "start",
|
||||
PROGRESS: "progress",
|
||||
EXTRACT: "extract",
|
||||
FINISH: "finish",
|
||||
INFO: "info",
|
||||
ERROR: "error"
|
||||
};
|
||||
|
||||
/**
|
||||
* Useful for passing info up to the client (for debugging).
|
||||
*
|
||||
* @param {string} msg The info message.
|
||||
*/
|
||||
bitjs.archive.UnarchiveInfoEvent = function(msg) {
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO);
|
||||
|
||||
/**
|
||||
* The information message.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.msg = msg;
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
/**
|
||||
* An unrecoverable error has occured.
|
||||
*
|
||||
* @param {string} msg The error message.
|
||||
*/
|
||||
bitjs.archive.UnarchiveErrorEvent = function(msg) {
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR);
|
||||
|
||||
/**
|
||||
* The information message.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.msg = msg;
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
/**
|
||||
* Start event.
|
||||
*
|
||||
* @param {string} msg The info message.
|
||||
*/
|
||||
bitjs.archive.UnarchiveStartEvent = function() {
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START);
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
/**
|
||||
* Finish event.
|
||||
*
|
||||
* @param {string} msg The info message.
|
||||
*/
|
||||
bitjs.archive.UnarchiveFinishEvent = function() {
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH);
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
/**
|
||||
* Progress event.
|
||||
*/
|
||||
bitjs.archive.UnarchiveProgressEvent = function(
|
||||
currentFilename,
|
||||
currentFileNumber,
|
||||
currentBytesUnarchivedInFile,
|
||||
currentBytesUnarchived,
|
||||
totalUncompressedBytesInArchive,
|
||||
totalFilesInArchive)
|
||||
{
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS);
|
||||
|
||||
this.currentFilename = currentFilename;
|
||||
this.currentFileNumber = currentFileNumber;
|
||||
this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile;
|
||||
this.totalFilesInArchive = totalFilesInArchive;
|
||||
this.currentBytesUnarchived = currentBytesUnarchived;
|
||||
this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive;
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
/**
|
||||
* All extracted files returned by an Unarchiver will implement
|
||||
* the following interface:
|
||||
*
|
||||
* interface UnarchivedFile {
|
||||
* string filename
|
||||
* TypedArray fileData
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extract event.
|
||||
*/
|
||||
bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) {
|
||||
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT);
|
||||
|
||||
/**
|
||||
* @type {UnarchivedFile}
|
||||
*/
|
||||
this.unarchivedFile = unarchivedFile;
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent);
|
||||
|
||||
|
||||
/**
|
||||
* Base class for all Unarchivers.
|
||||
*
|
||||
* @param {ArrayBuffer} arrayBuffer The Array Buffer.
|
||||
* @param {string} optPathToBitJS Optional string for where the BitJS files are located.
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.archive.Unarchiver = function(arrayBuffer, optPathToBitJS) {
|
||||
/**
|
||||
* The ArrayBuffer object.
|
||||
* @type {ArrayBuffer}
|
||||
* @protected
|
||||
*/
|
||||
this.ab = arrayBuffer;
|
||||
|
||||
/**
|
||||
* The path to the BitJS files.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.pathToBitJS_ = optPathToBitJS || "";
|
||||
|
||||
/**
|
||||
* A map from event type to an array of listeners.
|
||||
* @type {Map.<string, Array>}
|
||||
*/
|
||||
this.listeners_ = {};
|
||||
for (var type in bitjs.archive.UnarchiveEvent.Type) {
|
||||
this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Private web worker initialized during start().
|
||||
* @type {Worker}
|
||||
* @private
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.worker_ = null;
|
||||
|
||||
/**
|
||||
* This method must be overridden by the subclass to return the script filename.
|
||||
* @return {string} The script filename.
|
||||
* @protected.
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.getScriptFileName = function() {
|
||||
throw "Subclasses of AbstractUnarchiver must overload getScriptFileName()";
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds an event listener for UnarchiveEvents.
|
||||
*
|
||||
* @param {string} Event type.
|
||||
* @param {function} An event handler function.
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) {
|
||||
if (type in this.listeners_) {
|
||||
if (this.listeners_[type].indexOf(listener) === -1) {
|
||||
this.listeners_[type].push(listener);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes an event listener.
|
||||
*
|
||||
* @param {string} Event type.
|
||||
* @param {EventListener|function} An event listener or handler function.
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) {
|
||||
if (type in this.listeners_) {
|
||||
var index = this.listeners_[type].indexOf(listener);
|
||||
if (index !== -1) {
|
||||
this.listeners_[type].splice(index, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Receive an event and pass it to the listener functions.
|
||||
*
|
||||
* @param {bitjs.archive.UnarchiveEvent} e
|
||||
* @private
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) {
|
||||
if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) &&
|
||||
this.listeners_[e.type] instanceof Array) {
|
||||
this.listeners_[e.type].forEach(function (listener) {
|
||||
listener(e);
|
||||
});
|
||||
if (e.type === bitjs.archive.UnarchiveEvent.Type.FINISH) {
|
||||
this.worker_.terminate();
|
||||
}
|
||||
} else {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the unarchive in a separate Web Worker thread and returns immediately.
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.start = function() {
|
||||
var me = this;
|
||||
var scriptFileName = this.pathToBitJS_ + this.getScriptFileName();
|
||||
if (scriptFileName) {
|
||||
this.worker_ = new Worker(scriptFileName);
|
||||
|
||||
this.worker_.onerror = function(e) {
|
||||
console.log("Worker error: message = " + e.message);
|
||||
throw e;
|
||||
};
|
||||
|
||||
this.worker_.onmessage = function(e) {
|
||||
if (typeof e.data === "string") {
|
||||
// Just log any strings the workers pump our way.
|
||||
console.log(e.data);
|
||||
} else {
|
||||
// Assume that it is an UnarchiveEvent. Some browsers preserve the 'type'
|
||||
// so that instanceof UnarchiveEvent returns true, but others do not.
|
||||
me.handleWorkerEvent_(e.data);
|
||||
}
|
||||
};
|
||||
|
||||
this.worker_.postMessage({file: this.ab});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Terminates the Web Worker for this Unarchiver and returns immediately.
|
||||
*/
|
||||
bitjs.archive.Unarchiver.prototype.stop = function() {
|
||||
if (this.worker_) {
|
||||
this.worker_.terminate();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unzipper
|
||||
* @extends {bitjs.archive.Unarchiver}
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.archive.Unzipper = function(arrayBuffer, optPathToBitJS) {
|
||||
bitjs.base(this, arrayBuffer, optPathToBitJS);
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver);
|
||||
bitjs.archive.Unzipper.prototype.getScriptFileName = function() {
|
||||
return "unzip.js";
|
||||
};
|
||||
|
||||
/**
|
||||
* Unrarrer
|
||||
* @extends {bitjs.archive.Unarchiver}
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.archive.Unrarrer = function(arrayBuffer, optPathToBitJS) {
|
||||
bitjs.base(this, arrayBuffer, optPathToBitJS);
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver);
|
||||
bitjs.archive.Unrarrer.prototype.getScriptFileName = function() {
|
||||
return "unrar.js";
|
||||
};
|
||||
|
||||
/**
|
||||
* Untarrer
|
||||
* @extends {bitjs.archive.Unarchiver}
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.archive.Untarrer = function(arrayBuffer, optPathToBitJS) {
|
||||
bitjs.base(this, arrayBuffer, optPathToBitJS);
|
||||
};
|
||||
bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver);
|
||||
bitjs.archive.Untarrer.prototype.getScriptFileName = function() {
|
||||
return "untar.js";
|
||||
};
|
||||
|
||||
})();
|
@ -4,31 +4,31 @@
|
||||
/* global Bloodhound, language, Modernizr, tinymce */
|
||||
|
||||
if ($("#description").length) {
|
||||
tinymce.init({
|
||||
selector: "#description",
|
||||
branding: false,
|
||||
menubar: "edit view format",
|
||||
language
|
||||
});
|
||||
tinymce.init({
|
||||
selector: "#description",
|
||||
branding: false,
|
||||
menubar: "edit view format",
|
||||
language: language
|
||||
});
|
||||
|
||||
if (!Modernizr.inputtypes.date) {
|
||||
$("#pubdate").datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
language: language
|
||||
}).on("change", function () {
|
||||
// Show localized date over top of the standard YYYY-MM-DD date
|
||||
var pubDate;
|
||||
var results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD
|
||||
if (results) {
|
||||
pubDate = new Date(results[1], parseInt(results[2], 10) - 1, results[3]) || new Date(this.value);
|
||||
$("#fake_pubdate")
|
||||
.val(pubDate.toLocaleDateString(language))
|
||||
.removeClass("hidden");
|
||||
}
|
||||
}).trigger("change");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Modernizr.inputtypes.date) {
|
||||
$("#pubdate").datepicker({
|
||||
format: "yyyy-mm-dd",
|
||||
language
|
||||
}).on("change", function () {
|
||||
// Show localized date over top of the standard YYYY-MM-DD date
|
||||
let pubDate;
|
||||
const results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD
|
||||
if (results) {
|
||||
pubDate = new Date(results[1], parseInt(results[2], 10)-1, results[3]) || new Date(this.value);
|
||||
}
|
||||
$("#fake_pubdate")
|
||||
.val(pubDate.toLocaleDateString(language))
|
||||
.removeClass("hidden");
|
||||
}).trigger("change");
|
||||
}
|
||||
}
|
||||
/*
|
||||
Takes a prefix, query typeahead callback, Bloodhound typeahead adapter
|
||||
and returns the completions it gets from the bloodhound engine prefixed.
|
||||
@ -43,6 +43,7 @@ function prefixedSource(prefix, query, cb, bhAdapter) {
|
||||
cb(matches);
|
||||
});
|
||||
}
|
||||
|
||||
function getPath() {
|
||||
var jsFileLocation = $("script[src*=edit_books]").attr("src"); // the js file path
|
||||
jsFileLocation = jsFileLocation.replace("/static/js/edit_books.js", ""); // the js folder path
|
||||
@ -56,7 +57,7 @@ var authors = new Bloodhound({
|
||||
},
|
||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||
remote: {
|
||||
url: getPath()+"/get_authors_json?q=%QUERY"
|
||||
url: getPath() + "/get_authors_json?q=%QUERY"
|
||||
}
|
||||
});
|
||||
|
||||
@ -69,9 +70,9 @@ var series = new Bloodhound({
|
||||
return [query];
|
||||
},
|
||||
remote: {
|
||||
url: getPath()+"/get_series_json?q=",
|
||||
url: getPath() + "/get_series_json?q=",
|
||||
replace: function replace(url, query) {
|
||||
return url+encodeURIComponent(query);
|
||||
return url + encodeURIComponent(query);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -84,11 +85,11 @@ var tags = new Bloodhound({
|
||||
},
|
||||
queryTokenizer: function queryTokenizer(query) {
|
||||
var tokens = query.split(",");
|
||||
tokens = [tokens[tokens.length-1].trim()];
|
||||
tokens = [tokens[tokens.length - 1].trim()];
|
||||
return tokens;
|
||||
},
|
||||
remote: {
|
||||
url: getPath()+"/get_tags_json?q=%QUERY"
|
||||
url: getPath() + "/get_tags_json?q=%QUERY"
|
||||
}
|
||||
});
|
||||
|
||||
@ -101,9 +102,9 @@ var languages = new Bloodhound({
|
||||
return [query];
|
||||
},
|
||||
remote: {
|
||||
url: getPath()+"/get_languages_json?q=",
|
||||
url: getPath() + "/get_languages_json?q=",
|
||||
replace: function replace(url, query) {
|
||||
return url+encodeURIComponent(query);
|
||||
return url + encodeURIComponent(query);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -112,9 +113,9 @@ function sourceSplit(query, cb, split, source) {
|
||||
var bhAdapter = source.ttAdapter();
|
||||
|
||||
var tokens = query.split(split);
|
||||
var currentSource = tokens[tokens.length-1].trim();
|
||||
var currentSource = tokens[tokens.length - 1].trim();
|
||||
|
||||
tokens.splice(tokens.length-1, 1); // remove last element
|
||||
tokens.splice(tokens.length - 1, 1); // remove last element
|
||||
var prefix = "";
|
||||
var newSplit;
|
||||
if (split === "&") {
|
||||
@ -192,7 +193,7 @@ promiseLanguages.done(function() {
|
||||
|
||||
$("#search").on("change input.typeahead:selected", function() {
|
||||
var form = $("form").serialize();
|
||||
$.getJSON( getPath()+"/get_matching_tags", form, function( data ) {
|
||||
$.getJSON( getPath() + "/get_matching_tags", form, function( data ) {
|
||||
$(".tags_click").each(function() {
|
||||
if ($.inArray(parseInt($(this).children("input").first().val(), 10), data.tags) === -1 ) {
|
||||
if (!($(this).hasClass("active"))) {
|
||||
@ -204,3 +205,11 @@ $("#search").on("change input.typeahead:selected", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$("#btn-upload-format").on("change", function () {
|
||||
var filename = $(this).val();
|
||||
if (filename.substring(3, 11) === "fakepath") {
|
||||
filename = filename.substring(12);
|
||||
} // Remove c:\fake at beginning from localhost chrome
|
||||
$("#upload-format").html(filename);
|
||||
});
|
||||
|
@ -3,8 +3,8 @@
|
||||
* Created by idalin<dalin.lin@gmail.com>
|
||||
* Google Books api document: https://developers.google.com/books/docs/v1/using
|
||||
* Douban Books api document: https://developers.douban.com/wiki/?title=book_v2 (Chinese Only)
|
||||
*/
|
||||
/* global _, i18nMsg, tinymce */
|
||||
*/
|
||||
/* global _, i18nMsg, tinymce */
|
||||
var dbResults = [];
|
||||
var ggResults = [];
|
||||
|
||||
@ -103,6 +103,10 @@ $(function () {
|
||||
}
|
||||
};
|
||||
|
||||
if (book.rating > 0) {
|
||||
book.rating /= 2;
|
||||
}
|
||||
|
||||
var $book = $(templates.bookResult(book));
|
||||
$book.find("img").on("click", function () {
|
||||
populateForm(book);
|
||||
|
484
cps/static/js/io.js
Normal file
484
cps/static/js/io.js
Normal file
@ -0,0 +1,484 @@
|
||||
/*
|
||||
* io.js
|
||||
*
|
||||
* Provides readers for bit/byte streams (reading) and a byte buffer (writing).
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
* Copyright(c) 2011 antimatter15
|
||||
*/
|
||||
|
||||
/* global bitjs, Uint8Array */
|
||||
|
||||
var bitjs = bitjs || {};
|
||||
bitjs.io = bitjs.io || {};
|
||||
|
||||
(function() {
|
||||
|
||||
// mask for getting the Nth bit (zero-based)
|
||||
bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08,
|
||||
0x10, 0x20, 0x40, 0x80,
|
||||
0x100, 0x200, 0x400, 0x800,
|
||||
0x1000, 0x2000, 0x4000, 0x8000];
|
||||
|
||||
// mask for getting N number of bits (0-8)
|
||||
var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ];
|
||||
|
||||
|
||||
/**
|
||||
* This bit stream peeks and consumes bits out of a binary stream.
|
||||
*
|
||||
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
|
||||
* @param {boolean} rtl Whether the stream reads bits from the byte starting
|
||||
* from bit 7 to 0 (true) or bit 0 to 7 (false).
|
||||
* @param {Number} optOffset The offset into the ArrayBuffer
|
||||
* @param {Number} optLength The length of this BitStream
|
||||
*/
|
||||
bitjs.io.BitStream = function(ab, rtl, optOffset, optLength) {
|
||||
if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") {
|
||||
throw "Error! BitArray constructed with an invalid ArrayBuffer object";
|
||||
}
|
||||
|
||||
var offset = optOffset || 0;
|
||||
var length = optLength || ab.byteLength;
|
||||
this.bytes = new Uint8Array(ab, offset, length);
|
||||
this.bytePtr = 0; // tracks which byte we are on
|
||||
this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7)
|
||||
this.peekBits = rtl ? this.peekBitsRtl : this.peekBitsLtr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* byte0 byte1 byte2 byte3
|
||||
* 7......0 | 7......0 | 7......0 | 7......0
|
||||
*
|
||||
* The bit pointer starts at bit0 of byte0 and moves left until it reaches
|
||||
* bit7 of byte0, then jumps to bit0 of byte1, etc.
|
||||
* @param {number} n The number of bits to peek.
|
||||
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
|
||||
* @return {number} The peeked bits, as an unsigned number.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.peekBitsLtr = function(n, movePointers) {
|
||||
if (n <= 0 || typeof n !== typeof 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var movePointers = movePointers || false;
|
||||
var bytePtr = this.bytePtr;
|
||||
var bitPtr = this.bitPtr;
|
||||
var result = 0;
|
||||
var bitsIn = 0;
|
||||
var bytes = this.bytes;
|
||||
|
||||
// keep going until we have no more bits left to peek at
|
||||
// TODO: Consider putting all bits from bytes we will need into a variable and then
|
||||
// shifting/masking it to just extract the bits we want.
|
||||
// This could be considerably faster when reading more than 3 or 4 bits at a time.
|
||||
while (n > 0) {
|
||||
if (bytePtr >= bytes.length) {
|
||||
throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
|
||||
bytes.length + ", bitPtr=" + bitPtr;
|
||||
}
|
||||
|
||||
var numBitsLeftInThisByte = (8 - bitPtr);
|
||||
var mask;
|
||||
if (n >= numBitsLeftInThisByte) {
|
||||
mask = (BITMASK[numBitsLeftInThisByte] << bitPtr);
|
||||
result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
|
||||
|
||||
bytePtr++;
|
||||
bitPtr = 0;
|
||||
bitsIn += numBitsLeftInThisByte;
|
||||
n -= numBitsLeftInThisByte;
|
||||
} else {
|
||||
mask = (BITMASK[n] << bitPtr);
|
||||
result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
|
||||
|
||||
bitPtr += n;
|
||||
bitsIn += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (movePointers) {
|
||||
this.bitPtr = bitPtr;
|
||||
this.bytePtr = bytePtr;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* byte0 byte1 byte2 byte3
|
||||
* 7......0 | 7......0 | 7......0 | 7......0
|
||||
*
|
||||
* The bit pointer starts at bit7 of byte0 and moves right until it reaches
|
||||
* bit0 of byte0, then goes to bit7 of byte1, etc.
|
||||
* @param {number} n The number of bits to peek.
|
||||
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
|
||||
* @return {number} The peeked bits, as an unsigned number.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.peekBitsRtl = function(n, movePointers) {
|
||||
if (n <= 0 || typeof n != typeof 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var movePointers = movePointers || false;
|
||||
var bytePtr = this.bytePtr;
|
||||
var bitPtr = this.bitPtr;
|
||||
var result = 0;
|
||||
var bytes = this.bytes;
|
||||
|
||||
// keep going until we have no more bits left to peek at
|
||||
// TODO: Consider putting all bits from bytes we will need into a variable and then
|
||||
// shifting/masking it to just extract the bits we want.
|
||||
// This could be considerably faster when reading more than 3 or 4 bits at a time.
|
||||
while (n > 0) {
|
||||
|
||||
if (bytePtr >= bytes.length) {
|
||||
throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
|
||||
bytes.length + ", bitPtr=" + bitPtr;
|
||||
// return -1;
|
||||
}
|
||||
|
||||
var numBitsLeftInThisByte = (8 - bitPtr);
|
||||
if (n >= numBitsLeftInThisByte) {
|
||||
result <<= numBitsLeftInThisByte;
|
||||
result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]);
|
||||
bytePtr++;
|
||||
bitPtr = 0;
|
||||
n -= numBitsLeftInThisByte;
|
||||
}
|
||||
else {
|
||||
result <<= n;
|
||||
result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr));
|
||||
|
||||
bitPtr += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (movePointers) {
|
||||
this.bitPtr = bitPtr;
|
||||
this.bytePtr = bytePtr;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Some voodoo magic.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.getBits = function() {
|
||||
return (((((this.bytes[this.bytePtr] & 0xff) << 16) +
|
||||
((this.bytes[this.bytePtr + 1] & 0xff) << 8) +
|
||||
((this.bytes[this.bytePtr + 2] & 0xff))) >>> (8 - this.bitPtr)) & 0xffff);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reads n bits out of the stream, consuming them (moving the bit pointer).
|
||||
* @param {number} n The number of bits to read.
|
||||
* @return {number} The read bits, as an unsigned number.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.readBits = function(n) {
|
||||
return this.peekBits(n, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This returns n bytes as a sub-array, advancing the pointer if movePointers
|
||||
* is true. Only use this for uncompressed blocks as this throws away remaining
|
||||
* bits in the current byte.
|
||||
* @param {number} n The number of bytes to peek.
|
||||
* @param {boolean=} movePointers Whether to move the pointer, defaults false.
|
||||
* @return {Uint8Array} The subarray.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) {
|
||||
if (n <= 0 || typeof n != typeof 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// from http://tools.ietf.org/html/rfc1951#page-11
|
||||
// "Any bits of input up to the next byte boundary are ignored."
|
||||
while (this.bitPtr !== 0) {
|
||||
this.readBits(1);
|
||||
}
|
||||
|
||||
movePointers = movePointers || false;
|
||||
var bytePtr = this.bytePtr;
|
||||
// var bitPtr = this.bitPtr;
|
||||
|
||||
var result = this.bytes.subarray(bytePtr, bytePtr + n);
|
||||
|
||||
if (movePointers) {
|
||||
this.bytePtr += n;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {Uint8Array} The subarray.
|
||||
*/
|
||||
bitjs.io.BitStream.prototype.readBytes = function(n) {
|
||||
return this.peekBytes(n, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This object allows you to peek and consume bytes as numbers and strings
|
||||
* out of an ArrayBuffer. In this buffer, everything must be byte-aligned.
|
||||
*
|
||||
* @param {ArrayBuffer} ab The ArrayBuffer object.
|
||||
* @param {number=} optOffset The offset into the ArrayBuffer
|
||||
* @param {number=} optLength The length of this BitStream
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.io.ByteStream = function(ab, optOffset, optLength) {
|
||||
var offset = optOffset || 0;
|
||||
var length = optLength || ab.byteLength;
|
||||
this.bytes = new Uint8Array(ab, offset, length);
|
||||
this.ptr = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Peeks at the next n bytes as an unsigned number but does not advance the
|
||||
* pointer
|
||||
* TODO: This apparently cannot read more than 4 bytes as a number?
|
||||
* @param {number} n The number of bytes to peek at.
|
||||
* @return {number} The n bytes interpreted as an unsigned number.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.peekNumber = function(n) {
|
||||
// TODO: return error if n would go past the end of the stream?
|
||||
if (n <= 0 || typeof n !== typeof 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var result = 0;
|
||||
// read from last byte to first byte and roll them in
|
||||
var curByte = this.ptr + n - 1;
|
||||
while (curByte >= this.ptr) {
|
||||
result <<= 8;
|
||||
result |= this.bytes[curByte];
|
||||
--curByte;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next n bytes as an unsigned number (or -1 on error)
|
||||
* and advances the stream pointer n bytes.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {number} The n bytes interpreted as an unsigned number.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.readNumber = function(n) {
|
||||
var num = this.peekNumber( n );
|
||||
this.ptr += n;
|
||||
return num;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next n bytes as a signed number but does not advance the
|
||||
* pointer.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {number} The bytes interpreted as a signed number.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) {
|
||||
var num = this.peekNumber(n);
|
||||
var HALF = Math.pow(2, (n * 8) - 1);
|
||||
var FULL = HALF * 2;
|
||||
|
||||
if (num >= HALF) num -= FULL;
|
||||
|
||||
return num;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next n bytes as a signed number and advances the stream pointer.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {number} The bytes interpreted as a signed number.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.readSignedNumber = function(n) {
|
||||
var num = this.peekSignedNumber(n);
|
||||
this.ptr += n;
|
||||
return num;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This returns n bytes as a sub-array, advancing the pointer if movePointers
|
||||
* is true.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @param {boolean} movePointers Whether to move the pointers.
|
||||
* @return {Uint8Array} The subarray.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) {
|
||||
if (n <= 0 || typeof n != typeof 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = this.bytes.subarray(this.ptr, this.ptr + n);
|
||||
|
||||
if (movePointers) {
|
||||
this.ptr += n;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reads the next n bytes as a sub-array.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {Uint8Array} The subarray.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.readBytes = function(n) {
|
||||
return this.peekBytes(n, true);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Peeks at the next n bytes as a string but does not advance the pointer.
|
||||
* @param {number} n The number of bytes to peek at.
|
||||
* @return {string} The next n bytes as a string.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.peekString = function(n) {
|
||||
if (n <= 0 || typeof n != typeof 1) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var result = "";
|
||||
for (var p = this.ptr, end = this.ptr + n; p < end; ++p) {
|
||||
result += String.fromCharCode(this.bytes[p]);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next n bytes as an ASCII string and advances the stream pointer
|
||||
* n bytes.
|
||||
* @param {number} n The number of bytes to read.
|
||||
* @return {string} The next n bytes as a string.
|
||||
*/
|
||||
bitjs.io.ByteStream.prototype.readString = function(n) {
|
||||
var strToReturn = this.peekString(n);
|
||||
this.ptr += n;
|
||||
return strToReturn;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A write-only Byte buffer which uses a Uint8 Typed Array as a backing store.
|
||||
* @param {number} numBytes The number of bytes to allocate.
|
||||
* @constructor
|
||||
*/
|
||||
bitjs.io.ByteBuffer = function(numBytes) {
|
||||
if (typeof numBytes !== typeof 1 || numBytes <= 0) {
|
||||
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
|
||||
}
|
||||
this.data = new Uint8Array(numBytes);
|
||||
this.ptr = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} b The byte to insert.
|
||||
*/
|
||||
bitjs.io.ByteBuffer.prototype.insertByte = function(b) {
|
||||
// TODO: throw if byte is invalid?
|
||||
this.data[this.ptr++] = b;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert.
|
||||
*/
|
||||
bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) {
|
||||
// TODO: throw if bytes is invalid?
|
||||
this.data.set(bytes, this.ptr);
|
||||
this.ptr += bytes.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes an unsigned number into the next n bytes. If the number is too large
|
||||
* to fit into n bytes or is negative, an error is thrown.
|
||||
* @param {number} num The unsigned number to write.
|
||||
* @param {number} numBytes The number of bytes to write the number into.
|
||||
*/
|
||||
bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) {
|
||||
if (numBytes < 1) {
|
||||
throw "Trying to write into too few bytes: " + numBytes;
|
||||
}
|
||||
if (num < 0) {
|
||||
throw "Trying to write a negative number (" + num +
|
||||
") as an unsigned number to an ArrayBuffer";
|
||||
}
|
||||
if (num > (Math.pow(2, numBytes * 8) - 1)) {
|
||||
throw "Trying to write " + num + " into only " + numBytes + " bytes";
|
||||
}
|
||||
|
||||
// Roll 8-bits at a time into an array of bytes.
|
||||
var bytes = [];
|
||||
while (numBytes-- > 0) {
|
||||
var eightBits = num & 255;
|
||||
bytes.push(eightBits);
|
||||
num >>= 8;
|
||||
}
|
||||
|
||||
this.insertBytes(bytes);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a signed number into the next n bytes. If the number is too large
|
||||
* to fit into n bytes, an error is thrown.
|
||||
* @param {number} num The signed number to write.
|
||||
* @param {number} numBytes The number of bytes to write the number into.
|
||||
*/
|
||||
bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) {
|
||||
if (numBytes < 1) {
|
||||
throw "Trying to write into too few bytes: " + numBytes;
|
||||
}
|
||||
|
||||
var HALF = Math.pow(2, (numBytes * 8) - 1);
|
||||
if (num >= HALF || num < -HALF) {
|
||||
throw "Trying to write " + num + " into only " + numBytes + " bytes";
|
||||
}
|
||||
|
||||
// Roll 8-bits at a time into an array of bytes.
|
||||
var bytes = [];
|
||||
while (numBytes-- > 0) {
|
||||
var eightBits = num & 255;
|
||||
bytes.push(eightBits);
|
||||
num >>= 8;
|
||||
}
|
||||
|
||||
this.insertBytes(bytes);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} str The ASCII string to write.
|
||||
*/
|
||||
bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) {
|
||||
for (var i = 0; i < str.length; ++i) {
|
||||
var curByte = str.charCodeAt(i);
|
||||
if (curByte < 0 || curByte > 255) {
|
||||
throw "Trying to write a non-ASCII string!";
|
||||
}
|
||||
this.insertByte(curByte);
|
||||
}
|
||||
};
|
||||
})();
|
592
cps/static/js/kthoom.js
Normal file
592
cps/static/js/kthoom.js
Normal file
@ -0,0 +1,592 @@
|
||||
/*
|
||||
* kthoom.js
|
||||
*
|
||||
* Licensed under the MIT License
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
* Copyright(c) 2011 antimatter15
|
||||
*/
|
||||
|
||||
/* Reference Documentation:
|
||||
|
||||
* Web Workers: http://www.whatwg.org/specs/web-workers/current-work/
|
||||
* Web Workers in Mozilla: https://developer.mozilla.org/En/Using_web_workers
|
||||
* File API (FileReader): http://www.w3.org/TR/FileAPI/
|
||||
* Typed Arrays: http://www.khronos.org/registry/typedarray/specs/latest/#6
|
||||
|
||||
*/
|
||||
/* global bitjs */
|
||||
|
||||
if (window.opera) {
|
||||
window.console.log = function(str) {
|
||||
opera.postError(str);
|
||||
};
|
||||
}
|
||||
|
||||
var kthoom;
|
||||
|
||||
// gets the element with the given id
|
||||
function getElem(id) {
|
||||
if (document.documentElement.querySelector) {
|
||||
// querySelector lookup
|
||||
return document.body.querySelector("#" + id);
|
||||
}
|
||||
// getElementById lookup
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
if (window.kthoom === undefined) {
|
||||
kthoom = {};
|
||||
}
|
||||
|
||||
// key codes
|
||||
kthoom.Key = {
|
||||
ESCAPE: 27,
|
||||
LEFT: 37,
|
||||
UP: 38,
|
||||
RIGHT: 39,
|
||||
DOWN: 40,
|
||||
A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77,
|
||||
N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90,
|
||||
QUESTION_MARK: 191,
|
||||
LEFT_SQUARE_BRACKET: 219,
|
||||
RIGHT_SQUARE_BRACKET: 221
|
||||
};
|
||||
|
||||
// The rotation orientation of the comic.
|
||||
kthoom.rotateTimes = 0;
|
||||
|
||||
// global variables
|
||||
var unarchiver = null;
|
||||
var currentImage = 0;
|
||||
var imageFiles = [];
|
||||
var imageFilenames = [];
|
||||
var totalImages = 0;
|
||||
var lastCompletion = 0;
|
||||
|
||||
var hflip = false, vflip = false, fitMode = kthoom.Key.B;
|
||||
var canKeyNext = true, canKeyPrev = true;
|
||||
|
||||
kthoom.saveSettings = function() {
|
||||
localStorage.kthoomSettings = JSON.stringify({
|
||||
rotateTimes: kthoom.rotateTimes,
|
||||
hflip: hflip,
|
||||
vflip: vflip,
|
||||
fitMode: fitMode
|
||||
});
|
||||
};
|
||||
|
||||
kthoom.loadSettings = function() {
|
||||
try {
|
||||
if (localStorage.kthoomSettings.length < 10){
|
||||
return;
|
||||
}
|
||||
var s = JSON.parse(localStorage.kthoomSettings);
|
||||
kthoom.rotateTimes = s.rotateTimes;
|
||||
hflip = s.hflip;
|
||||
vflip = s.vflip;
|
||||
fitMode = s.fitMode;
|
||||
} catch (err) {
|
||||
alert("Error load settings");
|
||||
}
|
||||
};
|
||||
|
||||
var createURLFromArray = function(array, mimeType) {
|
||||
var offset = array.byteOffset, len = array.byteLength;
|
||||
var url;
|
||||
var blob;
|
||||
|
||||
// TODO: Move all this browser support testing to a common place
|
||||
// and do it just once.
|
||||
|
||||
// Blob constructor, see http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob.
|
||||
if (typeof Blob === "function") {
|
||||
blob = new Blob([array], {type: mimeType});
|
||||
} else {
|
||||
throw "Browser support for Blobs is missing.";
|
||||
}
|
||||
|
||||
if (blob.slice) {
|
||||
blob = blob.slice(offset, offset + len, mimeType);
|
||||
} else {
|
||||
throw "Browser support for Blobs is missing.";
|
||||
}
|
||||
|
||||
if ((typeof URL !== "function" && typeof URL !== "object") ||
|
||||
typeof URL.createObjectURL !== "function") {
|
||||
throw "Browser support for Object URLs is missing";
|
||||
}
|
||||
|
||||
return URL.createObjectURL(blob);
|
||||
};
|
||||
|
||||
|
||||
// Stores an image filename and its data: URI.
|
||||
// TODO: investigate if we really need to store as base64 (leave off ;base64 and just
|
||||
// non-safe URL characters are encoded as %xx ?)
|
||||
// This would save 25% on memory since base64-encoded strings are 4/3 the size of the binary
|
||||
kthoom.ImageFile = function(file) {
|
||||
this.filename = file.filename;
|
||||
var fileExtension = file.filename.split(".").pop().toLowerCase();
|
||||
var mimeType = fileExtension === "png" ? "image/png" :
|
||||
(fileExtension === "jpg" || fileExtension === "jpeg") ? "image/jpeg" :
|
||||
fileExtension === "gif" ? "image/gif" : null;
|
||||
this.dataURI = createURLFromArray(file.fileData, mimeType);
|
||||
this.data = file;
|
||||
};
|
||||
|
||||
|
||||
kthoom.initProgressMeter = function() {
|
||||
var svgns = "http://www.w3.org/2000/svg";
|
||||
var pdiv = $("#progress")[0];
|
||||
var svg = document.createElementNS(svgns, "svg");
|
||||
svg.style.width = "100%";
|
||||
svg.style.height = "100%";
|
||||
|
||||
var defs = document.createElementNS(svgns, "defs");
|
||||
|
||||
var patt = document.createElementNS(svgns, "pattern");
|
||||
patt.id = "progress_pattern";
|
||||
patt.setAttribute("width", "30");
|
||||
patt.setAttribute("height", "20");
|
||||
patt.setAttribute("patternUnits", "userSpaceOnUse");
|
||||
|
||||
var rect = document.createElementNS(svgns, "rect");
|
||||
rect.setAttribute("width", "100%");
|
||||
rect.setAttribute("height", "100%");
|
||||
rect.setAttribute("fill", "#cc2929");
|
||||
|
||||
var poly = document.createElementNS(svgns, "polygon");
|
||||
poly.setAttribute("fill", "yellow");
|
||||
poly.setAttribute("points", "15,0 30,0 15,20 0,20");
|
||||
|
||||
patt.appendChild(rect);
|
||||
patt.appendChild(poly);
|
||||
defs.appendChild(patt);
|
||||
|
||||
svg.appendChild(defs);
|
||||
|
||||
var g = document.createElementNS(svgns, "g");
|
||||
|
||||
var outline = document.createElementNS(svgns, "rect");
|
||||
outline.setAttribute("y", "1");
|
||||
outline.setAttribute("width", "100%");
|
||||
outline.setAttribute("height", "15");
|
||||
outline.setAttribute("fill", "#777");
|
||||
outline.setAttribute("stroke", "white");
|
||||
outline.setAttribute("rx", "5");
|
||||
outline.setAttribute("ry", "5");
|
||||
g.appendChild(outline);
|
||||
|
||||
var title = document.createElementNS(svgns, "text");
|
||||
title.id = "progress_title";
|
||||
title.appendChild(document.createTextNode("0%"));
|
||||
title.setAttribute("y", "13");
|
||||
title.setAttribute("x", "99.5%");
|
||||
title.setAttribute("fill", "white");
|
||||
title.setAttribute("font-size", "12px");
|
||||
title.setAttribute("text-anchor", "end");
|
||||
g.appendChild(title);
|
||||
|
||||
var meter = document.createElementNS(svgns, "rect");
|
||||
meter.id = "meter";
|
||||
meter.setAttribute("width", "0%");
|
||||
meter.setAttribute("height", "17");
|
||||
meter.setAttribute("fill", "url(#progress_pattern)");
|
||||
meter.setAttribute("rx", "5");
|
||||
meter.setAttribute("ry", "5");
|
||||
|
||||
var meter2 = document.createElementNS(svgns, "rect");
|
||||
meter2.id = "meter2";
|
||||
meter2.setAttribute("width", "0%");
|
||||
meter2.setAttribute("height", "17");
|
||||
meter2.setAttribute("opacity", "0.8");
|
||||
meter2.setAttribute("fill", "#007fff");
|
||||
meter2.setAttribute("rx", "5");
|
||||
meter2.setAttribute("ry", "5");
|
||||
|
||||
g.appendChild(meter);
|
||||
g.appendChild(meter2);
|
||||
|
||||
var page = document.createElementNS(svgns, "text");
|
||||
page.id = "page";
|
||||
page.appendChild(document.createTextNode("0/0"));
|
||||
page.setAttribute("y", "13");
|
||||
page.setAttribute("x", "0.5%");
|
||||
page.setAttribute("fill", "white");
|
||||
page.setAttribute("font-size", "12px");
|
||||
g.appendChild(page);
|
||||
|
||||
|
||||
svg.appendChild(g);
|
||||
pdiv.appendChild(svg);
|
||||
var l;
|
||||
svg.onclick = function(e) {
|
||||
for (var x = pdiv, l = 0; x !== document.documentElement; x = x.parentNode) l += x.offsetLeft;
|
||||
var page = Math.max(1, Math.ceil(((e.clientX - l) / pdiv.offsetWidth) * totalImages)) - 1;
|
||||
currentImage = page;
|
||||
updatePage();
|
||||
};
|
||||
}
|
||||
|
||||
kthoom.setProgressMeter = function(pct, optLabel) {
|
||||
pct = (pct * 100);
|
||||
var part = 1 / totalImages;
|
||||
var remain = ((pct - lastCompletion) / 100) / part;
|
||||
var fract = Math.min(1, remain);
|
||||
var smartpct = ((imageFiles.length / totalImages) + (fract * part)) * 100;
|
||||
if (totalImages === 0) smartpct = pct;
|
||||
|
||||
// + Math.min((pct - lastCompletion), 100/totalImages * 0.9 + (pct - lastCompletion - 100/totalImages)/2, 100/totalImages);
|
||||
var oldval = parseFloat(getElem("meter").getAttribute("width"));
|
||||
if (isNaN(oldval)) oldval = 0;
|
||||
var weight = 0.5;
|
||||
smartpct = ((weight * smartpct) + ((1 - weight) * oldval));
|
||||
if (pct === 100) smartpct = 100;
|
||||
|
||||
if (!isNaN(smartpct)) {
|
||||
getElem("meter").setAttribute("width", smartpct + "%");
|
||||
}
|
||||
var title = getElem("progress_title");
|
||||
while (title.firstChild) title.removeChild(title.firstChild);
|
||||
|
||||
var labelText = pct.toFixed(2) + "% " + imageFiles.length + "/" + totalImages + "";
|
||||
if (optLabel) {
|
||||
labelText = optLabel + " " + labelText;
|
||||
}
|
||||
title.appendChild(document.createTextNode(labelText));
|
||||
|
||||
getElem("meter2").setAttribute("width",
|
||||
100 * (totalImages === 0 ? 0 : ((currentImage + 1) / totalImages)) + "%");
|
||||
|
||||
var titlePage = getElem("page");
|
||||
while (titlePage.firstChild) titlePage.removeChild(titlePage.firstChild);
|
||||
titlePage.appendChild(document.createTextNode( (currentImage + 1) + "/" + totalImages ));
|
||||
|
||||
if (pct > 0) {
|
||||
//getElem('nav').className = '';
|
||||
getElem("progress").className = "";
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromArrayBuffer(ab) {
|
||||
var start = (new Date).getTime();
|
||||
var h = new Uint8Array(ab, 0, 10);
|
||||
var pathToBitJS = "../../static/js/";
|
||||
if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
|
||||
unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
|
||||
} else if (h[0] === 80 && h[1] === 75) { //PK (Zip)
|
||||
unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
|
||||
} else { // Try with tar
|
||||
unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS);
|
||||
}
|
||||
// Listen for UnarchiveEvents.
|
||||
if (unarchiver) {
|
||||
unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS,
|
||||
function(e) {
|
||||
var percentage = e.currentBytesUnarchived / e.totalUncompressedBytesInArchive;
|
||||
totalImages = e.totalFilesInArchive;
|
||||
kthoom.setProgressMeter(percentage, "Unzipping");
|
||||
// display nav
|
||||
lastCompletion = percentage * 100;
|
||||
});
|
||||
unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT,
|
||||
function(e) {
|
||||
// convert DecompressedFile into a bunch of ImageFiles
|
||||
if (e.unarchivedFile) {
|
||||
var f = e.unarchivedFile;
|
||||
// add any new pages based on the filename
|
||||
if (imageFilenames.indexOf(f.filename) === -1) {
|
||||
imageFilenames.push(f.filename);
|
||||
imageFiles.push(new kthoom.ImageFile(f));
|
||||
}
|
||||
}
|
||||
// display first page if we haven't yet
|
||||
if (imageFiles.length === currentImage + 1) {
|
||||
updatePage();
|
||||
}
|
||||
});
|
||||
unarchiver.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH,
|
||||
function() {
|
||||
var diff = ((new Date).getTime() - start) / 1000;
|
||||
console.log("Unarchiving done in " + diff + "s");
|
||||
});
|
||||
unarchiver.start();
|
||||
} else {
|
||||
alert("Some error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updatePage() {
|
||||
var title = getElem("page");
|
||||
while (title.firstChild) title.removeChild(title.firstChild);
|
||||
title.appendChild(document.createTextNode( (currentImage + 1 ) + "/" + totalImages ));
|
||||
|
||||
getElem("meter2").setAttribute("width",
|
||||
100 * (totalImages === 0 ? 0 : ((currentImage + 1 ) / totalImages)) + "%");
|
||||
if (imageFiles[currentImage]) {
|
||||
setImage(imageFiles[currentImage].dataURI);
|
||||
} else {
|
||||
setImage("loading");
|
||||
}
|
||||
}
|
||||
|
||||
function setImage(url) {
|
||||
var canvas = $("#mainImage")[0];
|
||||
var x = $("#mainImage")[0].getContext("2d");
|
||||
$("#mainText").hide();
|
||||
if (url === "loading") {
|
||||
updateScale(true);
|
||||
canvas.width = innerWidth - 100;
|
||||
canvas.height = 200;
|
||||
x.fillStyle = "red";
|
||||
x.font = "50px sans-serif";
|
||||
x.strokeStyle = "black";
|
||||
x.fillText("Loading Page #" + (currentImage + 1), 100, 100);
|
||||
} else {
|
||||
if ($("body").css("scrollHeight") / innerHeight > 1) {
|
||||
$("body").css("overflowY", "scroll");
|
||||
}
|
||||
|
||||
var img = new Image();
|
||||
img.onerror = function() {
|
||||
canvas.width = innerWidth - 100;
|
||||
canvas.height = 300;
|
||||
updateScale(true);
|
||||
x.fillStyle = "orange";
|
||||
x.font = "50px sans-serif";
|
||||
x.strokeStyle = "black";
|
||||
x.fillText("Page #" + (currentImage + 1) + " (" +
|
||||
imageFiles[currentImage].filename + ")", 100, 100);
|
||||
x.fillStyle = "red";
|
||||
x.fillText("Is corrupt or not an image", 100, 200);
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
if (/(html|htm)$/.test(imageFiles[currentImage].filename)) {
|
||||
xhr.open("GET", url, true);
|
||||
xhr.onload = function() {
|
||||
//document.getElementById('mainText').style.display = '';
|
||||
$("#mainText").css("display", "");
|
||||
$("#mainText").innerHTML("<iframe style=\"width:100%;height:700px;border:0\" src=\"data:text/html," + escape(xhr.responseText) + "\"></iframe>");
|
||||
}
|
||||
xhr.send(null);
|
||||
} else if (!/(jpg|jpeg|png|gif)$/.test(imageFiles[currentImage].filename) && imageFiles[currentImage].data.uncompressedSize < 10 * 1024) {
|
||||
xhr.open("GET", url, true);
|
||||
xhr.onload = function() {
|
||||
$("#mainText").css("display", "");
|
||||
$("#mainText").innerText(xhr.responseText);
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
};
|
||||
img.onload = function() {
|
||||
var h = img.height,
|
||||
w = img.width,
|
||||
sw = w,
|
||||
sh = h;
|
||||
kthoom.rotateTimes = (4 + kthoom.rotateTimes) % 4;
|
||||
x.save();
|
||||
if (kthoom.rotateTimes % 2 === 1) {
|
||||
sh = w;
|
||||
sw = h;
|
||||
}
|
||||
canvas.height = sh;
|
||||
canvas.width = sw;
|
||||
x.translate(sw / 2, sh / 2);
|
||||
x.rotate(Math.PI / 2 * kthoom.rotateTimes);
|
||||
x.translate(-w / 2, -h / 2);
|
||||
if (vflip) {
|
||||
x.scale(1, -1);
|
||||
x.translate(0, -h);
|
||||
}
|
||||
if (hflip) {
|
||||
x.scale(-1, 1);
|
||||
x.translate(-w, 0);
|
||||
}
|
||||
canvas.style.display = "none";
|
||||
scrollTo(0, 0);
|
||||
x.drawImage(img, 0, 0);
|
||||
|
||||
updateScale();
|
||||
|
||||
canvas.style.display = "";
|
||||
$("body").css("overflowY", "");
|
||||
x.restore();
|
||||
};
|
||||
img.src = url;
|
||||
}
|
||||
}
|
||||
|
||||
function showPrevPage() {
|
||||
currentImage--;
|
||||
if (currentImage < 0) {
|
||||
// Freeze on the current page.
|
||||
currentImage++;
|
||||
} else {
|
||||
updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
function showNextPage() {
|
||||
currentImage++;
|
||||
if (currentImage >= Math.max(totalImages, imageFiles.length)) {
|
||||
// Freeze on the current page.
|
||||
currentImage--;
|
||||
} else {
|
||||
updatePage();
|
||||
}
|
||||
}
|
||||
|
||||
function updateScale(clear) {
|
||||
var mainImageStyle = getElem("mainImage").style;
|
||||
mainImageStyle.width = "";
|
||||
mainImageStyle.height = "";
|
||||
mainImageStyle.maxWidth = "";
|
||||
mainImageStyle.maxHeight = "";
|
||||
var maxheight = innerHeight - 15;
|
||||
if (!/main/.test(getElem("titlebar").className)) {
|
||||
maxheight -= 25;
|
||||
}
|
||||
if (clear || fitMode === kthoom.Key.N) {
|
||||
} else if (fitMode === kthoom.Key.B) {
|
||||
mainImageStyle.maxWidth = "100%";
|
||||
mainImageStyle.maxHeight = maxheight + "px";
|
||||
} else if (fitMode === kthoom.Key.H) {
|
||||
mainImageStyle.height = maxheight + "px";
|
||||
} else if (fitMode === kthoom.Key.W) {
|
||||
mainImageStyle.width = "100%";
|
||||
}
|
||||
kthoom.saveSettings();
|
||||
}
|
||||
|
||||
function keyHandler(evt) {
|
||||
var code = evt.keyCode;
|
||||
|
||||
if ($("#progress").css("display") === "none"){
|
||||
return;
|
||||
}
|
||||
canKeyNext = (($("body").css("offsetWidth") + $("body").css("scrollLeft")) / $("body").css("scrollWidth")) >= 1;
|
||||
canKeyPrev = (scrollX <= 0);
|
||||
|
||||
if (evt.ctrlKey || evt.shiftKey || evt.metaKey) return;
|
||||
switch (code) {
|
||||
case kthoom.Key.LEFT:
|
||||
if (canKeyPrev) showPrevPage();
|
||||
break;
|
||||
case kthoom.Key.RIGHT:
|
||||
if (canKeyNext) showNextPage();
|
||||
break;
|
||||
case kthoom.Key.L:
|
||||
kthoom.rotateTimes--;
|
||||
if (kthoom.rotateTimes < 0) {
|
||||
kthoom.rotateTimes = 3;
|
||||
}
|
||||
updatePage();
|
||||
break;
|
||||
case kthoom.Key.R:
|
||||
kthoom.rotateTimes++;
|
||||
if (kthoom.rotateTimes > 3) {
|
||||
kthoom.rotateTimes = 0;
|
||||
}
|
||||
updatePage();
|
||||
break;
|
||||
case kthoom.Key.F:
|
||||
if (!hflip && !vflip) {
|
||||
hflip = true;
|
||||
} else if (hflip === true) {
|
||||
vflip = true;
|
||||
hflip = false;
|
||||
} else if (vflip === true) {
|
||||
vflip = false;
|
||||
}
|
||||
updatePage();
|
||||
break;
|
||||
case kthoom.Key.W:
|
||||
fitMode = kthoom.Key.W;
|
||||
updateScale();
|
||||
break;
|
||||
case kthoom.Key.H:
|
||||
fitMode = kthoom.Key.H;
|
||||
updateScale();
|
||||
break;
|
||||
case kthoom.Key.B:
|
||||
fitMode = kthoom.Key.B;
|
||||
updateScale();
|
||||
break;
|
||||
case kthoom.Key.N:
|
||||
fitMode = kthoom.Key.N;
|
||||
updateScale();
|
||||
break;
|
||||
default:
|
||||
//console.log('KeyCode = ' + code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function init(filename) {
|
||||
if (!window.FileReader) {
|
||||
alert("Sorry, kthoom will not work with your browser because it does not support the File API. Please try kthoom with Chrome 12+ or Firefox 7+");
|
||||
} else {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open("GET", filename);
|
||||
request.responseType = "arraybuffer";
|
||||
request.setRequestHeader("X-Test", "test1");
|
||||
request.setRequestHeader("X-Test", "test2");
|
||||
request.addEventListener("load", function(event) {
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
loadFromArrayBuffer(request.response);
|
||||
} else {
|
||||
console.warn(request.statusText, request.responseText);
|
||||
}
|
||||
});
|
||||
request.send();
|
||||
kthoom.initProgressMeter();
|
||||
document.body.className += /AppleWebKit/.test(navigator.userAgent) ? " webkit" : "";
|
||||
updateScale(true);
|
||||
kthoom.loadSettings();
|
||||
$(document).keydown(keyHandler);
|
||||
|
||||
$(window).resize(function() {
|
||||
var f = (screen.width - innerWidth < 4 && screen.height - innerHeight < 4);
|
||||
getElem("titlebar").className = f ? "main" : "";
|
||||
updateScale();
|
||||
});
|
||||
|
||||
$("#mainImage").click(function(evt) {
|
||||
// Firefox does not support offsetX/Y so we have to manually calculate
|
||||
// where the user clicked in the image.
|
||||
var mainContentWidth = $("#mainContent").width();
|
||||
var mainContentHeight = $("#mainContent").height();
|
||||
var comicWidth = evt.target.clientWidth;
|
||||
var comicHeight = evt.target.clientHeight;
|
||||
var offsetX = (mainContentWidth - comicWidth) / 2;
|
||||
var offsetY = (mainContentHeight - comicHeight) / 2;
|
||||
var clickX = !!evt.offsetX ? evt.offsetX : (evt.clientX - offsetX);
|
||||
var clickY = !!evt.offsetY ? evt.offsetY : (evt.clientY - offsetY);
|
||||
|
||||
// Determine if the user clicked/tapped the left side or the
|
||||
// right side of the page.
|
||||
var clickedPrev = false;
|
||||
switch (kthoom.rotateTimes) {
|
||||
case 0:
|
||||
clickedPrev = clickX < (comicWidth / 2);
|
||||
break;
|
||||
case 1:
|
||||
clickedPrev = clickY < (comicHeight / 2);
|
||||
break;
|
||||
case 2:
|
||||
clickedPrev = clickX > (comicWidth / 2);
|
||||
break;
|
||||
case 3:
|
||||
clickedPrev = clickY > (comicHeight / 2);
|
||||
break;
|
||||
}
|
||||
if (clickedPrev) {
|
||||
showPrevPage();
|
||||
} else {
|
||||
showNextPage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
1
cps/static/js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js
vendored
Normal file
1
cps/static/js/libs/bootstrap-datepicker/locales/bootstrap-datepicker.it.min.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
!function(a){a.fn.datepicker.dates.it={days:["Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato"],daysShort:["Dom","Lun","Mar","Mer","Gio","Ven","Sab"],daysMin:["Do","Lu","Ma","Me","Gi","Ve","Sa"],months:["Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno","Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre"],monthsShort:["Gen","Feb","Mar","Apr","Mag","Giu","Lug","Ago","Set","Ott","Nov","Dic"],today:"Oggi",monthsTitle:"Mesi",clear:"Cancella",weekStart:1,format:"dd/mm/yyyy"}}(jQuery);
|
@ -63,13 +63,13 @@ $(function() {
|
||||
$(".load-more .row").infinitescroll({
|
||||
debug: false,
|
||||
navSelector : ".pagination",
|
||||
// selector for the paged navigation (it will be hidden)
|
||||
// selector for the paged navigation (it will be hidden)
|
||||
nextSelector : ".pagination a:last",
|
||||
// selector for the NEXT link (to page 2)
|
||||
// selector for the NEXT link (to page 2)
|
||||
itemSelector : ".load-more .book",
|
||||
animate : true,
|
||||
extraScrollPx: 300
|
||||
// selector for all items you'll retrieve
|
||||
// selector for all items you'll retrieve
|
||||
}, function(data) {
|
||||
$(".load-more .row").isotope( "appended", $(data), null );
|
||||
});
|
||||
|
43
cps/static/js/reading/epub.js
Normal file
43
cps/static/js/reading/epub.js
Normal file
@ -0,0 +1,43 @@
|
||||
/* global $, calibre, EPUBJS, ePubReader */
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
EPUBJS.filePath = calibre.filePath;
|
||||
EPUBJS.cssPath = calibre.cssPath;
|
||||
|
||||
var reader = ePubReader(calibre.bookUrl, {
|
||||
restore: true,
|
||||
bookmarks: calibre.bookmark ? [calibre.bookmark] : []
|
||||
});
|
||||
|
||||
if (calibre.useBookmarks) {
|
||||
reader.on("reader:bookmarked", updateBookmark.bind(reader, "add"));
|
||||
reader.on("reader:unbookmarked", updateBookmark.bind(reader, "remove"));
|
||||
} else {
|
||||
$("#bookmark, #show-Bookmarks").remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} action - Add or remove bookmark
|
||||
* @param {string|int} location - Location or zero
|
||||
*/
|
||||
function updateBookmark(action, location) {
|
||||
// Remove other bookmarks (there can only be one)
|
||||
if (action === "add") {
|
||||
this.settings.bookmarks.filter(function (bookmark) {
|
||||
return bookmark && bookmark !== location;
|
||||
}).map(function (bookmark) {
|
||||
this.removeBookmark(bookmark);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// Save to database
|
||||
$.ajax(calibre.bookmarkUrl, {
|
||||
method: "post",
|
||||
data: { bookmark: location || "" }
|
||||
}).fail(function (xhr, status, error) {
|
||||
alert(error);
|
||||
});
|
||||
}
|
||||
})();
|
891
cps/static/js/unrar.js
Normal file
891
cps/static/js/unrar.js
Normal file
@ -0,0 +1,891 @@
|
||||
/**
|
||||
* unrar.js
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
* Copyright(c) 2011 antimatter15
|
||||
*
|
||||
* Reference Documentation:
|
||||
*
|
||||
* http://kthoom.googlecode.com/hg/docs/unrar.html
|
||||
*/
|
||||
/* global bitjs, importScripts */
|
||||
|
||||
// This file expects to be invoked as a Worker (see onmessage below).
|
||||
importScripts("io.js");
|
||||
importScripts("archive.js");
|
||||
|
||||
// Progress variables.
|
||||
var currentFilename = "";
|
||||
var currentFileNumber = 0;
|
||||
var currentBytesUnarchivedInFile = 0;
|
||||
var currentBytesUnarchived = 0;
|
||||
var totalUncompressedBytesInArchive = 0;
|
||||
var totalFilesInArchive = 0;
|
||||
|
||||
// Helper functions.
|
||||
var info = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
|
||||
};
|
||||
var err = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
|
||||
};
|
||||
var postProgress = function() {
|
||||
postMessage(new bitjs.archive.UnarchiveProgressEvent(
|
||||
currentFilename,
|
||||
currentFileNumber,
|
||||
currentBytesUnarchivedInFile,
|
||||
currentBytesUnarchived,
|
||||
totalUncompressedBytesInArchive,
|
||||
totalFilesInArchive));
|
||||
};
|
||||
|
||||
// shows a byte value as its hex representation
|
||||
var nibble = "0123456789ABCDEF";
|
||||
var byteValueToHexString = function(num) {
|
||||
return nibble[num>>4] + nibble[num & 0xF];
|
||||
};
|
||||
var twoByteValueToHexString = function(num) {
|
||||
return nibble[(num>>12) & 0xF] + nibble[(num>>8) & 0xF] + nibble[(num>>4) & 0xF] + nibble[num & 0xF];
|
||||
};
|
||||
|
||||
|
||||
// Volume Types
|
||||
// MARK_HEAD = 0x72;
|
||||
var MAIN_HEAD = 0x73,
|
||||
FILE_HEAD = 0x74,
|
||||
// COMM_HEAD = 0x75,
|
||||
// AV_HEAD = 0x76,
|
||||
// SUB_HEAD = 0x77,
|
||||
// PROTECT_HEAD = 0x78,
|
||||
// SIGN_HEAD = 0x79,
|
||||
// NEWSUB_HEAD = 0x7a,
|
||||
ENDARC_HEAD = 0x7b;
|
||||
|
||||
// bstream is a bit stream
|
||||
var RarVolumeHeader = function(bstream) {
|
||||
|
||||
var headPos = bstream.bytePtr;
|
||||
// byte 1,2
|
||||
info("Rar Volume Header @" + bstream.bytePtr);
|
||||
|
||||
this.crc = bstream.readBits(16);
|
||||
info(" crc=" + this.crc);
|
||||
|
||||
// byte 3
|
||||
this.headType = bstream.readBits(8);
|
||||
info(" headType=" + this.headType);
|
||||
|
||||
// Get flags
|
||||
// bytes 4,5
|
||||
this.flags = {};
|
||||
this.flags.value = bstream.peekBits(16);
|
||||
|
||||
info(" flags=" + twoByteValueToHexString(this.flags.value));
|
||||
switch (this.headType) {
|
||||
case MAIN_HEAD:
|
||||
this.flags.MHD_VOLUME = !!bstream.readBits(1);
|
||||
this.flags.MHD_COMMENT = !!bstream.readBits(1);
|
||||
this.flags.MHD_LOCK = !!bstream.readBits(1);
|
||||
this.flags.MHD_SOLID = !!bstream.readBits(1);
|
||||
this.flags.MHD_PACK_COMMENT = !!bstream.readBits(1);
|
||||
this.flags.MHD_NEWNUMBERING = this.flags.MHD_PACK_COMMENT;
|
||||
this.flags.MHD_AV = !!bstream.readBits(1);
|
||||
this.flags.MHD_PROTECT = !!bstream.readBits(1);
|
||||
this.flags.MHD_PASSWORD = !!bstream.readBits(1);
|
||||
this.flags.MHD_FIRSTVOLUME = !!bstream.readBits(1);
|
||||
this.flags.MHD_ENCRYPTVER = !!bstream.readBits(1);
|
||||
bstream.readBits(6); // unused
|
||||
break;
|
||||
case FILE_HEAD:
|
||||
this.flags.LHD_SPLIT_BEFORE = !!bstream.readBits(1); // 0x0001
|
||||
this.flags.LHD_SPLIT_AFTER = !!bstream.readBits(1); // 0x0002
|
||||
this.flags.LHD_PASSWORD = !!bstream.readBits(1); // 0x0004
|
||||
this.flags.LHD_COMMENT = !!bstream.readBits(1); // 0x0008
|
||||
this.flags.LHD_SOLID = !!bstream.readBits(1); // 0x0010
|
||||
bstream.readBits(3); // unused
|
||||
this.flags.LHD_LARGE = !!bstream.readBits(1); // 0x0100
|
||||
this.flags.LHD_UNICODE = !!bstream.readBits(1); // 0x0200
|
||||
this.flags.LHD_SALT = !!bstream.readBits(1); // 0x0400
|
||||
this.flags.LHD_VERSION = !!bstream.readBits(1); // 0x0800
|
||||
this.flags.LHD_EXTTIME = !!bstream.readBits(1); // 0x1000
|
||||
this.flags.LHD_EXTFLAGS = !!bstream.readBits(1); // 0x2000
|
||||
bstream.readBits(2); // unused
|
||||
info(" LHD_SPLIT_BEFORE = " + this.flags.LHD_SPLIT_BEFORE);
|
||||
break;
|
||||
default:
|
||||
bstream.readBits(16);
|
||||
}
|
||||
|
||||
// byte 6,7
|
||||
this.headSize = bstream.readBits(16);
|
||||
info(" headSize=" + this.headSize);
|
||||
switch (this.headType) {
|
||||
case MAIN_HEAD:
|
||||
this.highPosAv = bstream.readBits(16);
|
||||
this.posAv = bstream.readBits(32);
|
||||
if (this.flags.MHD_ENCRYPTVER) {
|
||||
this.encryptVer = bstream.readBits(8);
|
||||
}
|
||||
info("Found MAIN_HEAD with highPosAv=" + this.highPosAv + ", posAv=" + this.posAv);
|
||||
break;
|
||||
case FILE_HEAD:
|
||||
this.packSize = bstream.readBits(32);
|
||||
this.unpackedSize = bstream.readBits(32);
|
||||
this.hostOS = bstream.readBits(8);
|
||||
this.fileCRC = bstream.readBits(32);
|
||||
this.fileTime = bstream.readBits(32);
|
||||
this.unpVer = bstream.readBits(8);
|
||||
this.method = bstream.readBits(8);
|
||||
this.nameSize = bstream.readBits(16);
|
||||
this.fileAttr = bstream.readBits(32);
|
||||
|
||||
if (this.flags.LHD_LARGE) {
|
||||
info("Warning: Reading in LHD_LARGE 64-bit size values");
|
||||
this.HighPackSize = bstream.readBits(32);
|
||||
this.HighUnpSize = bstream.readBits(32);
|
||||
} else {
|
||||
this.HighPackSize = 0;
|
||||
this.HighUnpSize = 0;
|
||||
if (this.unpackedSize == 0xffffffff) {
|
||||
this.HighUnpSize = 0x7fffffff;
|
||||
this.unpackedSize = 0xffffffff;
|
||||
}
|
||||
}
|
||||
this.fullPackSize = 0;
|
||||
this.fullUnpackSize = 0;
|
||||
this.fullPackSize |= this.HighPackSize;
|
||||
this.fullPackSize <<= 32;
|
||||
this.fullPackSize |= this.packSize;
|
||||
|
||||
// read in filename
|
||||
|
||||
this.filename = bstream.readBytes(this.nameSize);
|
||||
for (var _i = 0, _s = ""; _i < this.filename.length ; _i++) {
|
||||
_s += String.fromCharCode(this.filename[_i]);
|
||||
}
|
||||
|
||||
this.filename = _s;
|
||||
|
||||
if (this.flags.LHD_SALT) {
|
||||
info("Warning: Reading in 64-bit salt value");
|
||||
this.salt = bstream.readBits(64); // 8 bytes
|
||||
}
|
||||
|
||||
if (this.flags.LHD_EXTTIME) {
|
||||
// 16-bit flags
|
||||
var extTimeFlags = bstream.readBits(16);
|
||||
|
||||
// this is adapted straight out of arcread.cpp, Archive::ReadHeader()
|
||||
for (var I = 0; I < 4; ++I) {
|
||||
var rmode = extTimeFlags >> ((3 - I) * 4);
|
||||
if ((rmode & 8)==0)
|
||||
continue;
|
||||
if (I!=0) {
|
||||
bstream.readBits(16);
|
||||
}
|
||||
var count = (rmode & 3);
|
||||
for (var J = 0; J < count; ++J) {
|
||||
bstream.readBits(8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.flags.LHD_COMMENT) {
|
||||
info("Found a LHD_COMMENT");
|
||||
}
|
||||
|
||||
|
||||
while (headPos + this.headSize > bstream.bytePtr) bstream.readBits(1);
|
||||
|
||||
info("Found FILE_HEAD with packSize=" + this.packSize + ", unpackedSize= " + this.unpackedSize + ", hostOS=" + this.hostOS + ", unpVer=" + this.unpVer + ", method=" + this.method + ", filename=" + this.filename);
|
||||
|
||||
break;
|
||||
default:
|
||||
info("Found a header of type 0x" + byteValueToHexString(this.headType));
|
||||
// skip the rest of the header bytes (for now)
|
||||
bstream.readBytes( this.headSize - 7 );
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var BLOCK_LZ = 0;
|
||||
// BLOCK_PPM = 1;
|
||||
|
||||
var rLDecode = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224],
|
||||
rLBits = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5],
|
||||
rDBitLengthCounts = [4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12],
|
||||
rSDDecode = [0, 4, 8, 16, 32, 64, 128, 192],
|
||||
rSDBits = [2,2,3, 4, 5, 6, 6, 6];
|
||||
|
||||
var rDDecode = [0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32,
|
||||
48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072,
|
||||
4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, 98304,
|
||||
131072, 196608, 262144, 327680, 393216, 458752, 524288, 589824,
|
||||
655360, 720896, 786432, 851968, 917504, 983040];
|
||||
|
||||
var rDBits = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
|
||||
5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14,
|
||||
15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16];
|
||||
|
||||
var rLOW_DIST_REP_COUNT = 16;
|
||||
|
||||
var rNC = 299,
|
||||
rDC = 60,
|
||||
rLDC = 17,
|
||||
rRC = 28,
|
||||
rBC = 20,
|
||||
rHUFF_TABLE_SIZE = (rNC + rDC + rRC + rLDC);
|
||||
|
||||
var UnpBlockType = BLOCK_LZ;
|
||||
var UnpOldTable = new Array(rHUFF_TABLE_SIZE);
|
||||
|
||||
var BD = { //bitdecode
|
||||
DecodeLen: new Array(16),
|
||||
DecodePos: new Array(16),
|
||||
DecodeNum: new Array(rBC)
|
||||
};
|
||||
var LD = { //litdecode
|
||||
DecodeLen: new Array(16),
|
||||
DecodePos: new Array(16),
|
||||
DecodeNum: new Array(rNC)
|
||||
};
|
||||
var DD = { //distdecode
|
||||
DecodeLen: new Array(16),
|
||||
DecodePos: new Array(16),
|
||||
DecodeNum: new Array(rDC)
|
||||
};
|
||||
var LDD = { //low dist decode
|
||||
DecodeLen: new Array(16),
|
||||
DecodePos: new Array(16),
|
||||
DecodeNum: new Array(rLDC)
|
||||
};
|
||||
var RD = { //rep decode
|
||||
DecodeLen: new Array(16),
|
||||
DecodePos: new Array(16),
|
||||
DecodeNum: new Array(rRC)
|
||||
};
|
||||
|
||||
var rBuffer;
|
||||
|
||||
// read in Huffman tables for RAR
|
||||
function RarReadTables(bstream) {
|
||||
var BitLength = new Array(rBC),
|
||||
Table = new Array(rHUFF_TABLE_SIZE);
|
||||
|
||||
// before we start anything we need to get byte-aligned
|
||||
bstream.readBits( (8 - bstream.bitPtr) & 0x7 );
|
||||
|
||||
if (bstream.readBits(1)) {
|
||||
info("Error! PPM not implemented yet");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bstream.readBits(1)) { //discard old table
|
||||
for (var i = UnpOldTable.length; i--;) UnpOldTable[i] = 0;
|
||||
}
|
||||
|
||||
// read in bit lengths
|
||||
for (var I = 0; I < rBC; ++I) {
|
||||
|
||||
var Length = bstream.readBits(4);
|
||||
if (Length == 15) {
|
||||
var ZeroCount = bstream.readBits(4);
|
||||
if (ZeroCount == 0) {
|
||||
BitLength[I] = 15;
|
||||
}
|
||||
else {
|
||||
ZeroCount += 2;
|
||||
while (ZeroCount-- > 0 && I < rBC)
|
||||
BitLength[I++] = 0;
|
||||
--I;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BitLength[I] = Length;
|
||||
}
|
||||
}
|
||||
|
||||
// now all 20 bit lengths are obtained, we construct the Huffman Table:
|
||||
|
||||
RarMakeDecodeTables(BitLength, 0, BD, rBC);
|
||||
|
||||
var TableSize = rHUFF_TABLE_SIZE;
|
||||
//console.log(DecodeLen, DecodePos, DecodeNum);
|
||||
for (var i = 0; i < TableSize;) {
|
||||
var num = RarDecodeNumber(bstream, BD);
|
||||
if (num < 16) {
|
||||
Table[i] = (num + UnpOldTable[i]) & 0xf;
|
||||
i++;
|
||||
} else if(num < 18) {
|
||||
var N = (num == 16) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
|
||||
|
||||
while (N-- > 0 && i < TableSize) {
|
||||
Table[i] = Table[i - 1];
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
var N = (num == 18) ? (bstream.readBits(3) + 3) : (bstream.readBits(7) + 11);
|
||||
|
||||
while (N-- > 0 && i < TableSize) {
|
||||
Table[i++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RarMakeDecodeTables(Table, 0, LD, rNC);
|
||||
RarMakeDecodeTables(Table, rNC, DD, rDC);
|
||||
RarMakeDecodeTables(Table, rNC + rDC, LDD, rLDC);
|
||||
RarMakeDecodeTables(Table, rNC + rDC + rLDC, RD, rRC);
|
||||
|
||||
for (var i = UnpOldTable.length; i--;) {
|
||||
UnpOldTable[i] = Table[i];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function RarDecodeNumber(bstream, dec) {
|
||||
var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum;
|
||||
var bitField = bstream.getBits() & 0xfffe;
|
||||
//some sort of rolled out binary search
|
||||
var bits = ((bitField < DecodeLen[8])?
|
||||
((bitField < DecodeLen[4])?
|
||||
((bitField < DecodeLen[2])?
|
||||
((bitField < DecodeLen[1])?1:2)
|
||||
:((bitField < DecodeLen[3])?3:4))
|
||||
:(bitField < DecodeLen[6])?
|
||||
((bitField < DecodeLen[5])?5:6)
|
||||
:((bitField < DecodeLen[7])?7:8))
|
||||
:((bitField < DecodeLen[12])?
|
||||
((bitField < DecodeLen[10])?
|
||||
((bitField < DecodeLen[9])?9:10)
|
||||
:((bitField < DecodeLen[11])?11:12))
|
||||
:(bitField < DecodeLen[14])?
|
||||
((bitField < DecodeLen[13])?13:14)
|
||||
:15));
|
||||
bstream.readBits(bits);
|
||||
var N = DecodePos[bits] + ((bitField - DecodeLen[bits -1]) >>> (16 - bits));
|
||||
|
||||
return DecodeNum[N];
|
||||
}
|
||||
|
||||
|
||||
function RarMakeDecodeTables(BitLength, offset, dec, size) {
|
||||
var DecodeLen = dec.DecodeLen, DecodePos = dec.DecodePos, DecodeNum = dec.DecodeNum;
|
||||
var LenCount = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
TmpPos = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
N = 0, M = 0;
|
||||
for (var i = DecodeNum.length; i--;) DecodeNum[i] = 0;
|
||||
for (var i = 0; i < size; i++) {
|
||||
LenCount[BitLength[i + offset] & 0xF]++;
|
||||
}
|
||||
LenCount[0] = 0;
|
||||
TmpPos[0] = 0;
|
||||
DecodePos[0] = 0;
|
||||
DecodeLen[0] = 0;
|
||||
|
||||
for (var I = 1; I < 16; ++I) {
|
||||
N = 2 * (N+LenCount[I]);
|
||||
M = (N << (15-I));
|
||||
if (M > 0xFFFF)
|
||||
M = 0xFFFF;
|
||||
DecodeLen[I] = M;
|
||||
DecodePos[I] = DecodePos[I-1] + LenCount[I-1];
|
||||
TmpPos[I] = DecodePos[I];
|
||||
}
|
||||
for (I = 0; I < size; ++I)
|
||||
if (BitLength[I + offset] != 0)
|
||||
DecodeNum[ TmpPos[ BitLength[offset + I] & 0xF ]++] = I;
|
||||
}
|
||||
|
||||
// TODO: implement
|
||||
function Unpack15(bstream, Solid) {
|
||||
info("ERROR! RAR 1.5 compression not supported");
|
||||
}
|
||||
|
||||
function Unpack20(bstream, Solid) {
|
||||
var destUnpSize = rBuffer.data.length;
|
||||
var oldDistPtr = 0;
|
||||
|
||||
RarReadTables20(bstream);
|
||||
while (destUnpSize > rBuffer.ptr) {
|
||||
var num = RarDecodeNumber(bstream, LD);
|
||||
if (num < 256) {
|
||||
rBuffer.insertByte(num);
|
||||
continue;
|
||||
}
|
||||
if (num > 269) {
|
||||
var Length = rLDecode[num -= 270] + 3;
|
||||
if ((Bits = rLBits[num]) > 0) {
|
||||
Length += bstream.readBits(Bits);
|
||||
}
|
||||
var DistNumber = RarDecodeNumber(bstream, DD);
|
||||
var Distance = rDDecode[DistNumber] + 1;
|
||||
if ((Bits = rDBits[DistNumber]) > 0) {
|
||||
Distance += bstream.readBits(Bits);
|
||||
}
|
||||
if (Distance >= 0x2000) {
|
||||
Length++;
|
||||
if(Distance >= 0x40000) Length++;
|
||||
}
|
||||
lastLength = Length;
|
||||
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
|
||||
RarCopyString(Length, Distance);
|
||||
continue;
|
||||
}
|
||||
if (num == 269) {
|
||||
RarReadTables20(bstream);
|
||||
|
||||
RarUpdateProgress()
|
||||
|
||||
continue;
|
||||
}
|
||||
if (num == 256) {
|
||||
lastDist = rOldDist[oldDistPtr++ & 3] = lastDist;
|
||||
RarCopyString(lastLength, lastDist);
|
||||
continue;
|
||||
}
|
||||
if (num < 261) {
|
||||
var Distance = rOldDist[(oldDistPtr - (num - 256)) & 3];
|
||||
var LengthNumber = RarDecodeNumber(bstream, RD);
|
||||
var Length = rLDecode[LengthNumber] +2;
|
||||
if ((Bits = rLBits[LengthNumber]) > 0) {
|
||||
Length += bstream.readBits(Bits);
|
||||
}
|
||||
if (Distance >= 0x101) {
|
||||
Length++;
|
||||
if (Distance >= 0x2000) {
|
||||
Length++
|
||||
if (Distance >= 0x40000) Length++;
|
||||
}
|
||||
}
|
||||
lastLength = Length;
|
||||
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
|
||||
RarCopyString(Length, Distance);
|
||||
continue;
|
||||
}
|
||||
if (num < 270) {
|
||||
var Distance = rSDDecode[num -= 261] + 1;
|
||||
if ((Bits = rSDBits[num]) > 0) {
|
||||
Distance += bstream.readBits(Bits);
|
||||
}
|
||||
lastLength = 2;
|
||||
lastDist = rOldDist[oldDistPtr++ & 3] = Distance;
|
||||
RarCopyString(2, Distance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RarUpdateProgress()
|
||||
}
|
||||
|
||||
function RarUpdateProgress() {
|
||||
var change = rBuffer.ptr - currentBytesUnarchivedInFile;
|
||||
currentBytesUnarchivedInFile = rBuffer.ptr;
|
||||
currentBytesUnarchived += change;
|
||||
postProgress();
|
||||
}
|
||||
|
||||
|
||||
var rNC20 = 298,
|
||||
rDC20 = 48,
|
||||
rRC20 = 28,
|
||||
rBC20 = 19,
|
||||
rMC20 = 257;
|
||||
|
||||
var UnpOldTable20 = new Array(rMC20 * 4);
|
||||
|
||||
function RarReadTables20(bstream) {
|
||||
var BitLength = new Array(rBC20);
|
||||
var Table = new Array(rMC20 * 4);
|
||||
var TableSize, N, I;
|
||||
var AudioBlock = bstream.readBits(1);
|
||||
if (!bstream.readBits(1))
|
||||
for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = 0;
|
||||
TableSize = rNC20 + rDC20 + rRC20;
|
||||
for (var I = 0; I < rBC20; I++)
|
||||
BitLength[I] = bstream.readBits(4);
|
||||
RarMakeDecodeTables(BitLength, 0, BD, rBC20);
|
||||
I = 0;
|
||||
while (I < TableSize) {
|
||||
var num = RarDecodeNumber(bstream, BD);
|
||||
if (num < 16) {
|
||||
Table[I] = num + UnpOldTable20[I] & 0xf;
|
||||
I++;
|
||||
} else if(num == 16) {
|
||||
N = bstream.readBits(2) + 3;
|
||||
while (N-- > 0 && I < TableSize) {
|
||||
Table[I] = Table[I - 1];
|
||||
I++;
|
||||
}
|
||||
} else {
|
||||
if (num == 17) {
|
||||
N = bstream.readBits(3) + 3;
|
||||
} else {
|
||||
N = bstream.readBits(7) + 11;
|
||||
}
|
||||
while (N-- > 0 && I < TableSize) {
|
||||
Table[I++] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
RarMakeDecodeTables(Table, 0, LD, rNC20);
|
||||
RarMakeDecodeTables(Table, rNC20, DD, rDC20);
|
||||
RarMakeDecodeTables(Table, rNC20 + rDC20, RD, rRC20);
|
||||
for (var i = UnpOldTable20.length; i--;) UnpOldTable20[i] = Table[i];
|
||||
}
|
||||
|
||||
var lowDistRepCount = 0, prevLowDist = 0;
|
||||
|
||||
var rOldDist = [0,0,0,0];
|
||||
var lastDist;
|
||||
var lastLength;
|
||||
|
||||
|
||||
function Unpack29(bstream, Solid) {
|
||||
// lazy initialize rDDecode and rDBits
|
||||
|
||||
var DDecode = new Array(rDC);
|
||||
var DBits = new Array(rDC);
|
||||
|
||||
var Dist=0,BitLength=0,Slot=0;
|
||||
|
||||
for (var I = 0; I < rDBitLengthCounts.length; I++,BitLength++) {
|
||||
for (var J = 0; J < rDBitLengthCounts[I]; J++,Slot++,Dist+=(1<<BitLength)) {
|
||||
DDecode[Slot]=Dist;
|
||||
DBits[Slot]=BitLength;
|
||||
}
|
||||
}
|
||||
|
||||
var Bits;
|
||||
//tablesRead = false;
|
||||
|
||||
rOldDist = [0,0,0,0]
|
||||
|
||||
lastDist = 0;
|
||||
lastLength = 0;
|
||||
|
||||
for (var i = UnpOldTable.length; i--;) UnpOldTable[i] = 0;
|
||||
|
||||
// read in Huffman tables
|
||||
RarReadTables(bstream);
|
||||
|
||||
while (true) {
|
||||
var num = RarDecodeNumber(bstream, LD);
|
||||
|
||||
if (num < 256) {
|
||||
rBuffer.insertByte(num);
|
||||
continue;
|
||||
}
|
||||
if (num >= 271) {
|
||||
var Length = rLDecode[num -= 271] + 3;
|
||||
if ((Bits = rLBits[num]) > 0) {
|
||||
Length += bstream.readBits(Bits);
|
||||
}
|
||||
var DistNumber = RarDecodeNumber(bstream, DD);
|
||||
var Distance = DDecode[DistNumber]+1;
|
||||
if ((Bits = DBits[DistNumber]) > 0) {
|
||||
if (DistNumber > 9) {
|
||||
if (Bits > 4) {
|
||||
Distance += ((bstream.getBits() >>> (20 - Bits)) << 4);
|
||||
bstream.readBits(Bits - 4);
|
||||
//todo: check this
|
||||
}
|
||||
if (lowDistRepCount > 0) {
|
||||
lowDistRepCount--;
|
||||
Distance += prevLowDist;
|
||||
} else {
|
||||
var LowDist = RarDecodeNumber(bstream, LDD);
|
||||
if (LowDist == 16) {
|
||||
lowDistRepCount = rLOW_DIST_REP_COUNT - 1;
|
||||
Distance += prevLowDist;
|
||||
} else {
|
||||
Distance += LowDist;
|
||||
prevLowDist = LowDist;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Distance += bstream.readBits(Bits);
|
||||
}
|
||||
}
|
||||
if (Distance >= 0x2000) {
|
||||
Length++;
|
||||
if (Distance >= 0x40000) {
|
||||
Length++;
|
||||
}
|
||||
}
|
||||
RarInsertOldDist(Distance);
|
||||
RarInsertLastMatch(Length, Distance);
|
||||
RarCopyString(Length, Distance);
|
||||
continue;
|
||||
}
|
||||
if (num == 256) {
|
||||
if (!RarReadEndOfBlock(bstream)) break;
|
||||
continue;
|
||||
}
|
||||
if (num == 257) {
|
||||
//console.log("READVMCODE");
|
||||
if (!RarReadVMCode(bstream)) break;
|
||||
continue;
|
||||
}
|
||||
if (num == 258) {
|
||||
if (lastLength != 0) {
|
||||
RarCopyString(lastLength, lastDist);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (num < 263) {
|
||||
var DistNum = num - 259;
|
||||
var Distance = rOldDist[DistNum];
|
||||
|
||||
for (var I = DistNum; I > 0; I--) {
|
||||
rOldDist[I] = rOldDist[I-1];
|
||||
}
|
||||
rOldDist[0] = Distance;
|
||||
|
||||
var LengthNumber = RarDecodeNumber(bstream, RD);
|
||||
var Length = rLDecode[LengthNumber] + 2;
|
||||
if ((Bits = rLBits[LengthNumber]) > 0) {
|
||||
Length += bstream.readBits(Bits);
|
||||
}
|
||||
RarInsertLastMatch(Length, Distance);
|
||||
RarCopyString(Length, Distance);
|
||||
continue;
|
||||
}
|
||||
if (num < 272) {
|
||||
var Distance = rSDDecode[num -= 263] + 1;
|
||||
if ((Bits = rSDBits[num]) > 0) {
|
||||
Distance += bstream.readBits(Bits);
|
||||
}
|
||||
RarInsertOldDist(Distance);
|
||||
RarInsertLastMatch(2, Distance);
|
||||
RarCopyString(2, Distance);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
RarUpdateProgress()
|
||||
}
|
||||
|
||||
function RarReadEndOfBlock(bstream) {
|
||||
|
||||
RarUpdateProgress()
|
||||
|
||||
var NewTable = false, NewFile = false;
|
||||
if (bstream.readBits(1)) {
|
||||
NewTable = true;
|
||||
} else {
|
||||
NewFile = true;
|
||||
NewTable = !!bstream.readBits(1);
|
||||
}
|
||||
//tablesRead = !NewTable;
|
||||
return !(NewFile || NewTable && !RarReadTables(bstream));
|
||||
}
|
||||
|
||||
|
||||
function RarReadVMCode(bstream) {
|
||||
var FirstByte = bstream.readBits(8);
|
||||
var Length = (FirstByte & 7) + 1;
|
||||
if (Length == 7) {
|
||||
Length = bstream.readBits(8) + 7;
|
||||
} else if(Length == 8) {
|
||||
Length = bstream.readBits(16);
|
||||
}
|
||||
var vmCode = [];
|
||||
for(var I = 0; I < Length; I++) {
|
||||
//do something here with cheking readbuf
|
||||
vmCode.push(bstream.readBits(8));
|
||||
}
|
||||
return RarAddVMCode(FirstByte, vmCode, Length);
|
||||
}
|
||||
|
||||
function RarAddVMCode(firstByte, vmCode, length) {
|
||||
//console.log(vmCode);
|
||||
if (vmCode.length > 0) {
|
||||
info("Error! RarVM not supported yet!");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function RarInsertLastMatch(length, distance) {
|
||||
lastDist = distance;
|
||||
lastLength = length;
|
||||
}
|
||||
|
||||
function RarInsertOldDist(distance) {
|
||||
rOldDist.splice(3,1);
|
||||
rOldDist.splice(0,0,distance);
|
||||
}
|
||||
|
||||
//this is the real function, the other one is for debugging
|
||||
function RarCopyString(length, distance) {
|
||||
var destPtr = rBuffer.ptr - distance;
|
||||
if(destPtr < 0){
|
||||
var l = rOldBuffers.length;
|
||||
while(destPtr < 0){
|
||||
destPtr = rOldBuffers[--l].data.length + destPtr;
|
||||
}
|
||||
//TODO: lets hope that it never needs to read beyond file boundaries
|
||||
while(length--) rBuffer.insertByte(rOldBuffers[l].data[destPtr++]);
|
||||
}
|
||||
if (length > distance) {
|
||||
while(length--) rBuffer.insertByte(rBuffer.data[destPtr++]);
|
||||
} else {
|
||||
rBuffer.insertBytes(rBuffer.data.subarray(destPtr, destPtr + length));
|
||||
}
|
||||
}
|
||||
|
||||
var rOldBuffers = []
|
||||
// v must be a valid RarVolume
|
||||
function unpack(v) {
|
||||
|
||||
// TODO: implement what happens when unpVer is < 15
|
||||
var Ver = v.header.unpVer <= 15 ? 15 : v.header.unpVer,
|
||||
Solid = v.header.LHD_SOLID,
|
||||
bstream = new bitjs.io.BitStream(v.fileData.buffer, true /* rtl */, v.fileData.byteOffset, v.fileData.byteLength );
|
||||
|
||||
rBuffer = new bitjs.io.ByteBuffer(v.header.unpackedSize);
|
||||
|
||||
info("Unpacking " + v.filename+" RAR v" + Ver);
|
||||
|
||||
switch(Ver) {
|
||||
case 15: // rar 1.5 compression
|
||||
Unpack15(bstream, Solid);
|
||||
break;
|
||||
case 20: // rar 2.x compression
|
||||
case 26: // files larger than 2GB
|
||||
Unpack20(bstream, Solid);
|
||||
break;
|
||||
case 29: // rar 3.x compression
|
||||
case 36: // alternative hash
|
||||
Unpack29(bstream, Solid);
|
||||
break;
|
||||
} // switch(method)
|
||||
|
||||
rOldBuffers.push(rBuffer);
|
||||
//TODO: clear these old buffers when there's over 4MB of history
|
||||
return rBuffer.data;
|
||||
}
|
||||
|
||||
// bstream is a bit stream
|
||||
var RarLocalFile = function(bstream) {
|
||||
|
||||
this.header = new RarVolumeHeader(bstream);
|
||||
this.filename = this.header.filename;
|
||||
|
||||
if (this.header.headType != FILE_HEAD && this.header.headType != ENDARC_HEAD) {
|
||||
this.isValid = false;
|
||||
info("Error! RAR Volume did not include a FILE_HEAD header ");
|
||||
}
|
||||
else {
|
||||
// read in the compressed data
|
||||
this.fileData = null;
|
||||
if (this.header.packSize > 0) {
|
||||
this.fileData = bstream.readBytes(this.header.packSize);
|
||||
this.isValid = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RarLocalFile.prototype.unrar = function() {
|
||||
|
||||
if (!this.header.flags.LHD_SPLIT_BEFORE) {
|
||||
// unstore file
|
||||
if (this.header.method == 0x30) {
|
||||
info("Unstore "+this.filename);
|
||||
this.isValid = true;
|
||||
|
||||
currentBytesUnarchivedInFile += this.fileData.length;
|
||||
currentBytesUnarchived += this.fileData.length;
|
||||
|
||||
// Create a new buffer and copy it over.
|
||||
var len = this.header.packSize;
|
||||
var newBuffer = new bitjs.io.ByteBuffer(len);
|
||||
newBuffer.insertBytes(this.fileData);
|
||||
this.fileData = newBuffer.data;
|
||||
} else {
|
||||
this.isValid = true;
|
||||
this.fileData = unpack(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unrar = function(arrayBuffer) {
|
||||
currentFilename = "";
|
||||
currentFileNumber = 0;
|
||||
currentBytesUnarchivedInFile = 0;
|
||||
currentBytesUnarchived = 0;
|
||||
totalUncompressedBytesInArchive = 0;
|
||||
totalFilesInArchive = 0;
|
||||
|
||||
postMessage(new bitjs.archive.UnarchiveStartEvent());
|
||||
var bstream = new bitjs.io.BitStream(arrayBuffer, false /* rtl */);
|
||||
|
||||
var header = new RarVolumeHeader(bstream);
|
||||
if (header.crc == 0x6152 &&
|
||||
header.headType == 0x72 &&
|
||||
header.flags.value == 0x1A21 &&
|
||||
header.headSize == 7)
|
||||
{
|
||||
info("Found RAR signature");
|
||||
|
||||
var mhead = new RarVolumeHeader(bstream);
|
||||
if (mhead.headType != MAIN_HEAD) {
|
||||
info("Error! RAR did not include a MAIN_HEAD header");
|
||||
} else {
|
||||
var localFiles = [],
|
||||
localFile = null;
|
||||
do {
|
||||
try {
|
||||
localFile = new RarLocalFile(bstream);
|
||||
info("RAR localFile isValid=" + localFile.isValid + ", volume packSize=" + localFile.header.packSize);
|
||||
if (localFile && localFile.isValid && localFile.header.packSize > 0) {
|
||||
totalUncompressedBytesInArchive += localFile.header.unpackedSize;
|
||||
localFiles.push(localFile);
|
||||
} else if (localFile.header.packSize == 0 && localFile.header.unpackedSize == 0) {
|
||||
localFile.isValid = true;
|
||||
}
|
||||
} catch(err) {
|
||||
break;
|
||||
}
|
||||
//info("bstream" + bstream.bytePtr+"/"+bstream.bytes.length);
|
||||
} while( localFile.isValid );
|
||||
totalFilesInArchive = localFiles.length;
|
||||
|
||||
// now we have all information but things are unpacked
|
||||
// TODO: unpack
|
||||
localFiles = localFiles.sort(function(a,b) {
|
||||
var aname = a.filename.toLowerCase();
|
||||
var bname = b.filename.toLowerCase();
|
||||
return aname > bname ? 1 : -1;
|
||||
});
|
||||
|
||||
info(localFiles.map(function(a){return a.filename}).join(', '));
|
||||
for (var i = 0; i < localFiles.length; ++i) {
|
||||
var localfile = localFiles[i];
|
||||
|
||||
// update progress
|
||||
currentFilename = localfile.header.filename;
|
||||
currentBytesUnarchivedInFile = 0;
|
||||
|
||||
// actually do the unzipping
|
||||
localfile.unrar();
|
||||
|
||||
if (localfile.isValid) {
|
||||
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
|
||||
postProgress();
|
||||
}
|
||||
}
|
||||
|
||||
postProgress();
|
||||
}
|
||||
}
|
||||
else {
|
||||
err("Invalid RAR file");
|
||||
}
|
||||
postMessage(new bitjs.archive.UnarchiveFinishEvent());
|
||||
};
|
||||
|
||||
// event.data.file has the ArrayBuffer.
|
||||
onmessage = function(event) {
|
||||
var ab = event.data.file;
|
||||
unrar(ab, true);
|
||||
};
|
168
cps/static/js/untar.js
Normal file
168
cps/static/js/untar.js
Normal file
@ -0,0 +1,168 @@
|
||||
/**
|
||||
* untar.js
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
*
|
||||
* Reference Documentation:
|
||||
*
|
||||
* TAR format: http://www.gnu.org/software/automake/manual/tar/Standard.html
|
||||
*/
|
||||
|
||||
// This file expects to be invoked as a Worker (see onmessage below).
|
||||
importScripts('io.js');
|
||||
importScripts('archive.js');
|
||||
|
||||
// Progress variables.
|
||||
var currentFilename = "";
|
||||
var currentFileNumber = 0;
|
||||
var currentBytesUnarchivedInFile = 0;
|
||||
var currentBytesUnarchived = 0;
|
||||
var totalUncompressedBytesInArchive = 0;
|
||||
var totalFilesInArchive = 0;
|
||||
|
||||
// Helper functions.
|
||||
var info = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
|
||||
};
|
||||
var err = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
|
||||
};
|
||||
var postProgress = function() {
|
||||
postMessage(new bitjs.archive.UnarchiveProgressEvent(
|
||||
currentFilename,
|
||||
currentFileNumber,
|
||||
currentBytesUnarchivedInFile,
|
||||
currentBytesUnarchived,
|
||||
totalUncompressedBytesInArchive,
|
||||
totalFilesInArchive));
|
||||
};
|
||||
|
||||
// Removes all characters from the first zero-byte in the string onwards.
|
||||
var readCleanString = function(bstr, numBytes) {
|
||||
var str = bstr.readString(numBytes);
|
||||
var zIndex = str.indexOf(String.fromCharCode(0));
|
||||
return zIndex != -1 ? str.substr(0, zIndex) : str;
|
||||
};
|
||||
|
||||
// takes a ByteStream and parses out the local file information
|
||||
var TarLocalFile = function(bstream) {
|
||||
this.isValid = false;
|
||||
|
||||
// Read in the header block
|
||||
this.name = readCleanString(bstream, 100);
|
||||
this.mode = readCleanString(bstream, 8);
|
||||
this.uid = readCleanString(bstream, 8);
|
||||
this.gid = readCleanString(bstream, 8);
|
||||
this.size = parseInt(readCleanString(bstream, 12), 8);
|
||||
this.mtime = readCleanString(bstream, 12);
|
||||
this.chksum = readCleanString(bstream, 8);
|
||||
this.typeflag = readCleanString(bstream, 1);
|
||||
this.linkname = readCleanString(bstream, 100);
|
||||
this.maybeMagic = readCleanString(bstream, 6);
|
||||
|
||||
if (this.maybeMagic == "ustar") {
|
||||
this.version = readCleanString(bstream, 2);
|
||||
this.uname = readCleanString(bstream, 32);
|
||||
this.gname = readCleanString(bstream, 32);
|
||||
this.devmajor = readCleanString(bstream, 8);
|
||||
this.devminor = readCleanString(bstream, 8);
|
||||
this.prefix = readCleanString(bstream, 155);
|
||||
|
||||
if (this.prefix.length) {
|
||||
this.name = this.prefix + this.name;
|
||||
}
|
||||
bstream.readBytes(12); // 512 - 500
|
||||
} else {
|
||||
bstream.readBytes(255); // 512 - 257
|
||||
}
|
||||
|
||||
// Done header, now rest of blocks are the file contents.
|
||||
this.filename = this.name;
|
||||
this.fileData = null;
|
||||
|
||||
info("Untarring file '" + this.filename + "'");
|
||||
info(" size = " + this.size);
|
||||
info(" typeflag = " + this.typeflag);
|
||||
|
||||
// A regular file.
|
||||
if (this.typeflag == 0) {
|
||||
info(" This is a regular file.");
|
||||
var sizeInBytes = parseInt(this.size);
|
||||
this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.size);
|
||||
if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) {
|
||||
this.isValid = true;
|
||||
}
|
||||
|
||||
bstream.readBytes(this.size);
|
||||
|
||||
// Round up to 512-byte blocks.
|
||||
var remaining = 512 - this.size % 512;
|
||||
if (remaining > 0 && remaining < 512) {
|
||||
bstream.readBytes(remaining);
|
||||
}
|
||||
} else if (this.typeflag == 5) {
|
||||
info(" This is a directory.")
|
||||
}
|
||||
};
|
||||
|
||||
// Takes an ArrayBuffer of a tar file in
|
||||
// returns null on error
|
||||
// returns an array of DecompressedFile objects on success
|
||||
var untar = function(arrayBuffer) {
|
||||
currentFilename = "";
|
||||
currentFileNumber = 0;
|
||||
currentBytesUnarchivedInFile = 0;
|
||||
currentBytesUnarchived = 0;
|
||||
totalUncompressedBytesInArchive = 0;
|
||||
totalFilesInArchive = 0;
|
||||
|
||||
postMessage(new bitjs.archive.UnarchiveStartEvent());
|
||||
var bstream = new bitjs.io.ByteStream(arrayBuffer);
|
||||
var localFiles = [];
|
||||
|
||||
// While we don't encounter an empty block, keep making TarLocalFiles.
|
||||
while (bstream.peekNumber(4) != 0) {
|
||||
var oneLocalFile = new TarLocalFile(bstream);
|
||||
if (oneLocalFile && oneLocalFile.isValid) {
|
||||
localFiles.push(oneLocalFile);
|
||||
totalUncompressedBytesInArchive += oneLocalFile.size;
|
||||
}
|
||||
}
|
||||
totalFilesInArchive = localFiles.length;
|
||||
|
||||
// got all local files, now sort them
|
||||
localFiles.sort(function(a,b) {
|
||||
var aname = a.filename.toLowerCase();
|
||||
var bname = b.filename.toLowerCase();
|
||||
return aname > bname ? 1 : -1;
|
||||
});
|
||||
|
||||
// report # files and total length
|
||||
if (localFiles.length > 0) {
|
||||
postProgress();
|
||||
}
|
||||
|
||||
// now do the shipping of each file
|
||||
for (var i = 0; i < localFiles.length; ++i) {
|
||||
var localfile = localFiles[i];
|
||||
info("Sending file '" + localfile.filename + "' up");
|
||||
|
||||
// update progress
|
||||
currentFilename = localfile.filename;
|
||||
currentFileNumber = i;
|
||||
currentBytesUnarchivedInFile = localfile.size;
|
||||
currentBytesUnarchived += localfile.size;
|
||||
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
|
||||
postProgress();
|
||||
}
|
||||
|
||||
postProgress();
|
||||
|
||||
postMessage(new bitjs.archive.UnarchiveFinishEvent());
|
||||
};
|
||||
|
||||
// event.data.file has the ArrayBuffer.
|
||||
onmessage = function(event) {
|
||||
var ab = event.data.file;
|
||||
untar(ab);
|
||||
};
|
621
cps/static/js/unzip.js
Normal file
621
cps/static/js/unzip.js
Normal file
@ -0,0 +1,621 @@
|
||||
/**
|
||||
* unzip.js
|
||||
*
|
||||
* Copyright(c) 2011 Google Inc.
|
||||
* Copyright(c) 2011 antimatter15
|
||||
*
|
||||
* Reference Documentation:
|
||||
*
|
||||
* ZIP format: http://www.pkware.com/documents/casestudies/APPNOTE.TXT
|
||||
* DEFLATE format: http://tools.ietf.org/html/rfc1951
|
||||
*/
|
||||
/* global bitjs, importScripts, Uint8Array */
|
||||
|
||||
// This file expects to be invoked as a Worker (see onmessage below).
|
||||
importScripts("io.js");
|
||||
importScripts("archive.js");
|
||||
|
||||
// Progress variables.
|
||||
var currentFilename = "";
|
||||
var currentFileNumber = 0;
|
||||
var currentBytesUnarchivedInFile = 0;
|
||||
var currentBytesUnarchived = 0;
|
||||
var totalUncompressedBytesInArchive = 0;
|
||||
var totalFilesInArchive = 0;
|
||||
|
||||
// Helper functions.
|
||||
var info = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
|
||||
};
|
||||
var err = function(str) {
|
||||
postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
|
||||
};
|
||||
var postProgress = function() {
|
||||
postMessage(new bitjs.archive.UnarchiveProgressEvent(
|
||||
currentFilename,
|
||||
currentFileNumber,
|
||||
currentBytesUnarchivedInFile,
|
||||
currentBytesUnarchived,
|
||||
totalUncompressedBytesInArchive,
|
||||
totalFilesInArchive));
|
||||
};
|
||||
|
||||
var zLocalFileHeaderSignature = 0x04034b50;
|
||||
var zArchiveExtraDataSignature = 0x08064b50;
|
||||
var zCentralFileHeaderSignature = 0x02014b50;
|
||||
var zDigitalSignatureSignature = 0x05054b50;
|
||||
//var zEndOfCentralDirSignature = 0x06064b50;
|
||||
//var zEndOfCentralDirLocatorSignature = 0x07064b50;
|
||||
|
||||
// takes a ByteStream and parses out the local file information
|
||||
var ZipLocalFile = function(bstream) {
|
||||
if (typeof bstream !== typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function() {} ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bstream.readNumber(4); // swallow signature
|
||||
this.version = bstream.readNumber(2);
|
||||
this.generalPurpose = bstream.readNumber(2);
|
||||
this.compressionMethod = bstream.readNumber(2);
|
||||
this.lastModFileTime = bstream.readNumber(2);
|
||||
this.lastModFileDate = bstream.readNumber(2);
|
||||
this.crc32 = bstream.readNumber(4);
|
||||
this.compressedSize = bstream.readNumber(4);
|
||||
this.uncompressedSize = bstream.readNumber(4);
|
||||
this.fileNameLength = bstream.readNumber(2);
|
||||
this.extraFieldLength = bstream.readNumber(2);
|
||||
|
||||
this.filename = null;
|
||||
if (this.fileNameLength > 0) {
|
||||
this.filename = bstream.readString(this.fileNameLength);
|
||||
}
|
||||
|
||||
info("Zip Local File Header:");
|
||||
info(" version=" + this.version);
|
||||
info(" general purpose=" + this.generalPurpose);
|
||||
info(" compression method=" + this.compressionMethod);
|
||||
info(" last mod file time=" + this.lastModFileTime);
|
||||
info(" last mod file date=" + this.lastModFileDate);
|
||||
info(" crc32=" + this.crc32);
|
||||
info(" compressed size=" + this.compressedSize);
|
||||
info(" uncompressed size=" + this.uncompressedSize);
|
||||
info(" file name length=" + this.fileNameLength);
|
||||
info(" extra field length=" + this.extraFieldLength);
|
||||
info(" filename = '" + this.filename + "'");
|
||||
|
||||
this.extraField = null;
|
||||
if (this.extraFieldLength > 0) {
|
||||
this.extraField = bstream.readString(this.extraFieldLength);
|
||||
info(" extra field=" + this.extraField);
|
||||
}
|
||||
|
||||
// read in the compressed data
|
||||
this.fileData = null;
|
||||
if (this.compressedSize > 0) {
|
||||
this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize);
|
||||
bstream.ptr += this.compressedSize;
|
||||
}
|
||||
|
||||
// TODO: deal with data descriptor if present (we currently assume no data descriptor!)
|
||||
// "This descriptor exists only if bit 3 of the general purpose bit flag is set"
|
||||
// But how do you figure out how big the file data is if you don't know the compressedSize
|
||||
// from the header?!?
|
||||
if ((this.generalPurpose & bitjs.BIT[3]) !== 0) {
|
||||
this.crc32 = bstream.readNumber(4);
|
||||
this.compressedSize = bstream.readNumber(4);
|
||||
this.uncompressedSize = bstream.readNumber(4);
|
||||
}
|
||||
};
|
||||
|
||||
// determine what kind of compressed data we have and decompress
|
||||
ZipLocalFile.prototype.unzip = function() {
|
||||
|
||||
// Zip Version 1.0, no compression (store only)
|
||||
if (this.compressionMethod === 0 ) {
|
||||
info("ZIP v" + this.version + ", store only: " + this.filename + " (" + this.compressedSize + " bytes)");
|
||||
currentBytesUnarchivedInFile = this.compressedSize;
|
||||
currentBytesUnarchived += this.compressedSize;
|
||||
}
|
||||
// version == 20, compression method == 8 (DEFLATE)
|
||||
else if (this.compressionMethod === 8) {
|
||||
info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)");
|
||||
this.fileData = inflate(this.fileData, this.uncompressedSize);
|
||||
}
|
||||
else {
|
||||
err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)");
|
||||
this.fileData = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Takes an ArrayBuffer of a zip file in
|
||||
// returns null on error
|
||||
// returns an array of DecompressedFile objects on success
|
||||
var unzip = function(arrayBuffer) {
|
||||
postMessage(new bitjs.archive.UnarchiveStartEvent());
|
||||
|
||||
currentFilename = "";
|
||||
currentFileNumber = 0;
|
||||
currentBytesUnarchivedInFile = 0;
|
||||
currentBytesUnarchived = 0;
|
||||
totalUncompressedBytesInArchive = 0;
|
||||
totalFilesInArchive = 0;
|
||||
currentBytesUnarchived = 0;
|
||||
|
||||
var bstream = new bitjs.io.ByteStream(arrayBuffer);
|
||||
// detect local file header signature or return null
|
||||
if (bstream.peekNumber(4) === zLocalFileHeaderSignature) {
|
||||
var localFiles = [];
|
||||
// loop until we don't see any more local files
|
||||
while (bstream.peekNumber(4) === zLocalFileHeaderSignature) {
|
||||
var oneLocalFile = new ZipLocalFile(bstream);
|
||||
// this should strip out directories/folders
|
||||
if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) {
|
||||
localFiles.push(oneLocalFile);
|
||||
totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize;
|
||||
}
|
||||
}
|
||||
totalFilesInArchive = localFiles.length;
|
||||
|
||||
// got all local files, now sort them
|
||||
localFiles.sort(function(a, b) {
|
||||
var aname = a.filename.toLowerCase();
|
||||
var bname = b.filename.toLowerCase();
|
||||
return aname > bname ? 1 : -1;
|
||||
});
|
||||
|
||||
// archive extra data record
|
||||
if (bstream.peekNumber(4) === zArchiveExtraDataSignature) {
|
||||
info(" Found an Archive Extra Data Signature");
|
||||
|
||||
// skipping this record for now
|
||||
bstream.readNumber(4);
|
||||
var archiveExtraFieldLength = bstream.readNumber(4);
|
||||
bstream.readString(archiveExtraFieldLength);
|
||||
}
|
||||
|
||||
// central directory structure
|
||||
// TODO: handle the rest of the structures (Zip64 stuff)
|
||||
if (bstream.peekNumber(4) === zCentralFileHeaderSignature) {
|
||||
info(" Found a Central File Header");
|
||||
|
||||
// read all file headers
|
||||
while (bstream.peekNumber(4) === zCentralFileHeaderSignature) {
|
||||
bstream.readNumber(4); // signature
|
||||
bstream.readNumber(2); // version made by
|
||||
bstream.readNumber(2); // version needed to extract
|
||||
bstream.readNumber(2); // general purpose bit flag
|
||||
bstream.readNumber(2); // compression method
|
||||
bstream.readNumber(2); // last mod file time
|
||||
bstream.readNumber(2); // last mod file date
|
||||
bstream.readNumber(4); // crc32
|
||||
bstream.readNumber(4); // compressed size
|
||||
bstream.readNumber(4); // uncompressed size
|
||||
var fileNameLength = bstream.readNumber(2); // file name length
|
||||
var extraFieldLength = bstream.readNumber(2); // extra field length
|
||||
var fileCommentLength = bstream.readNumber(2); // file comment length
|
||||
bstream.readNumber(2); // disk number start
|
||||
bstream.readNumber(2); // internal file attributes
|
||||
bstream.readNumber(4); // external file attributes
|
||||
bstream.readNumber(4); // relative offset of local header
|
||||
|
||||
bstream.readString(fileNameLength); // file name
|
||||
bstream.readString(extraFieldLength); // extra field
|
||||
bstream.readString(fileCommentLength); // file comment
|
||||
}
|
||||
}
|
||||
|
||||
// digital signature
|
||||
if (bstream.peekNumber(4) === zDigitalSignatureSignature) {
|
||||
info(" Found a Digital Signature");
|
||||
|
||||
bstream.readNumber(4);
|
||||
var sizeOfSignature = bstream.readNumber(2);
|
||||
bstream.readString(sizeOfSignature); // digital signature data
|
||||
}
|
||||
|
||||
// report # files and total length
|
||||
if (localFiles.length > 0) {
|
||||
postProgress();
|
||||
}
|
||||
|
||||
// now do the unzipping of each file
|
||||
for (var i = 0; i < localFiles.length; ++i) {
|
||||
var localfile = localFiles[i];
|
||||
|
||||
// update progress
|
||||
currentFilename = localfile.filename;
|
||||
currentFileNumber = i;
|
||||
currentBytesUnarchivedInFile = 0;
|
||||
|
||||
// actually do the unzipping
|
||||
localfile.unzip();
|
||||
|
||||
if (localfile.fileData !== null) {
|
||||
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
|
||||
postProgress();
|
||||
}
|
||||
}
|
||||
postProgress();
|
||||
postMessage(new bitjs.archive.UnarchiveFinishEvent());
|
||||
}
|
||||
};
|
||||
|
||||
// returns a table of Huffman codes
|
||||
// each entry's index is its code and its value is a JavaScript object
|
||||
// containing {length: 6, symbol: X}
|
||||
function getHuffmanCodes(bitLengths) {
|
||||
// ensure bitLengths is an array containing at least one element
|
||||
if (typeof bitLengths !== typeof [] || bitLengths.length < 1) {
|
||||
err("Error! getHuffmanCodes() called with an invalid array");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Reference: http://tools.ietf.org/html/rfc1951#page-8
|
||||
var numLengths = bitLengths.length,
|
||||
blCount = [],
|
||||
MAX_BITS = 1;
|
||||
|
||||
// Step 1: count up how many codes of each length we have
|
||||
for (var i = 0; i < numLengths; ++i) {
|
||||
var len = bitLengths[i];
|
||||
// test to ensure each bit length is a positive, non-zero number
|
||||
if (typeof len !== typeof 1 || len < 0) {
|
||||
err("bitLengths contained an invalid number in getHuffmanCodes(): " + len + " of type " + (typeof len));
|
||||
return null;
|
||||
}
|
||||
// increment the appropriate bitlength count
|
||||
if (blCount[len] === undefined) blCount[len] = 0;
|
||||
// a length of zero means this symbol is not participating in the huffman coding
|
||||
if (len > 0) blCount[len]++;
|
||||
|
||||
if (len > MAX_BITS) MAX_BITS = len;
|
||||
}
|
||||
|
||||
// Step 2: Find the numerical value of the smallest code for each code length
|
||||
var nextCode = [],
|
||||
code = 0;
|
||||
for (var bits = 1; bits <= MAX_BITS; ++bits) {
|
||||
var len = bits-1;
|
||||
// ensure undefined lengths are zero
|
||||
if (blCount[len] == undefined) blCount[len] = 0;
|
||||
code = (code + blCount[bits-1]) << 1;
|
||||
nextCode[bits] = code;
|
||||
}
|
||||
|
||||
// Step 3: Assign numerical values to all codes
|
||||
var table = {}, tableLength = 0;
|
||||
for (var n = 0; n < numLengths; ++n) {
|
||||
var len = bitLengths[n];
|
||||
if (len !== 0) {
|
||||
table[nextCode[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(nextCode[len],len) };
|
||||
tableLength++;
|
||||
nextCode[len]++;
|
||||
}
|
||||
}
|
||||
table.maxLength = tableLength;
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
/*
|
||||
The Huffman codes for the two alphabets are fixed, and are not
|
||||
represented explicitly in the data. The Huffman code lengths
|
||||
for the literal/length alphabet are:
|
||||
|
||||
Lit Value Bits Codes
|
||||
--------- ---- -----
|
||||
0 - 143 8 00110000 through
|
||||
10111111
|
||||
144 - 255 9 110010000 through
|
||||
111111111
|
||||
256 - 279 7 0000000 through
|
||||
0010111
|
||||
280 - 287 8 11000000 through
|
||||
11000111
|
||||
*/
|
||||
// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits
|
||||
var fixedHCtoLiteral = null;
|
||||
var fixedHCtoDistance = null;
|
||||
function getFixedLiteralTable() {
|
||||
// create once
|
||||
if (!fixedHCtoLiteral) {
|
||||
var bitlengths = new Array(288);
|
||||
for (var i = 0; i <= 143; ++i) bitlengths[i] = 8;
|
||||
for (var i = 144; i <= 255; ++i) bitlengths[i] = 9;
|
||||
for (var i = 256; i <= 279; ++i) bitlengths[i] = 7;
|
||||
for (var i = 280; i <= 287; ++i) bitlengths[i] = 8;
|
||||
|
||||
// get huffman code table
|
||||
fixedHCtoLiteral = getHuffmanCodes(bitlengths);
|
||||
}
|
||||
return fixedHCtoLiteral;
|
||||
}
|
||||
function getFixedDistanceTable() {
|
||||
// create once
|
||||
if (!fixedHCtoDistance) {
|
||||
var bitlengths = new Array(32);
|
||||
for (var i = 0; i < 32; ++i) {
|
||||
bitlengths[i] = 5;
|
||||
}
|
||||
|
||||
// get huffman code table
|
||||
fixedHCtoDistance = getHuffmanCodes(bitlengths);
|
||||
}
|
||||
return fixedHCtoDistance;
|
||||
}
|
||||
|
||||
// extract one bit at a time until we find a matching Huffman Code
|
||||
// then return that symbol
|
||||
function decodeSymbol(bstream, hcTable) {
|
||||
var code = 0, len = 0;
|
||||
// var match = false;
|
||||
|
||||
// loop until we match
|
||||
for (;;) {
|
||||
// read in next bit
|
||||
var bit = bstream.readBits(1);
|
||||
code = (code<<1) | bit;
|
||||
++len;
|
||||
|
||||
// check against Huffman Code table and break if found
|
||||
if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) {
|
||||
|
||||
break;
|
||||
}
|
||||
if (len > hcTable.maxLength) {
|
||||
err("Bit stream out of sync, didn't find a Huffman Code, length was " + len +
|
||||
" and table only max code length of " + hcTable.maxLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hcTable[code].symbol;
|
||||
}
|
||||
|
||||
|
||||
var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
||||
/*
|
||||
Extra Extra Extra
|
||||
Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
|
||||
---- ---- ------ ---- ---- ------- ---- ---- -------
|
||||
257 0 3 267 1 15,16 277 4 67-82
|
||||
258 0 4 268 1 17,18 278 4 83-98
|
||||
259 0 5 269 2 19-22 279 4 99-114
|
||||
260 0 6 270 2 23-26 280 4 115-130
|
||||
261 0 7 271 2 27-30 281 5 131-162
|
||||
262 0 8 272 2 31-34 282 5 163-194
|
||||
263 0 9 273 3 35-42 283 5 195-226
|
||||
264 0 10 274 3 43-50 284 5 227-257
|
||||
265 1 11,12 275 3 51-58 285 0 258
|
||||
266 1 13,14 276 3 59-66
|
||||
|
||||
*/
|
||||
var LengthLookupTable = [
|
||||
[0, 3], [0, 4], [0, 5], [0, 6],
|
||||
[0, 7], [0, 8], [0, 9], [0, 10],
|
||||
[1, 11], [1, 13], [1, 15], [1, 17],
|
||||
[2, 19], [2, 23], [2, 27], [2, 31],
|
||||
[3, 35], [3, 43], [3, 51], [3, 59],
|
||||
[4, 67], [4, 83], [4, 99], [4, 115],
|
||||
[5, 131], [5, 163], [5, 195], [5, 227],
|
||||
[0, 258]
|
||||
];
|
||||
/*
|
||||
Extra Extra Extra
|
||||
Code Bits Dist Code Bits Dist Code Bits Distance
|
||||
---- ---- ---- ---- ---- ------ ---- ---- --------
|
||||
0 0 1 10 4 33-48 20 9 1025-1536
|
||||
1 0 2 11 4 49-64 21 9 1537-2048
|
||||
2 0 3 12 5 65-96 22 10 2049-3072
|
||||
3 0 4 13 5 97-128 23 10 3073-4096
|
||||
4 1 5,6 14 6 129-192 24 11 4097-6144
|
||||
5 1 7,8 15 6 193-256 25 11 6145-8192
|
||||
6 2 9-12 16 7 257-384 26 12 8193-12288
|
||||
7 2 13-16 17 7 385-512 27 12 12289-16384
|
||||
8 3 17-24 18 8 513-768 28 13 16385-24576
|
||||
9 3 25-32 19 8 769-1024 29 13 24577-32768
|
||||
*/
|
||||
var DistLookupTable = [
|
||||
[0, 1], [0, 2], [0, 3], [0, 4],
|
||||
[1, 5], [1, 7],
|
||||
[2, 9], [2, 13],
|
||||
[3, 17], [3, 25],
|
||||
[4, 33], [4, 49],
|
||||
[5, 65], [5, 97],
|
||||
[6, 129], [6, 193],
|
||||
[7, 257], [7, 385],
|
||||
[8, 513], [8, 769],
|
||||
[9, 1025], [9, 1537],
|
||||
[10, 2049], [10, 3073],
|
||||
[11, 4097], [11, 6145],
|
||||
[12, 8193], [12, 12289],
|
||||
[13, 16385], [13, 24577]
|
||||
];
|
||||
|
||||
function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
|
||||
/*
|
||||
loop (until end of block code recognized)
|
||||
decode literal/length value from input stream
|
||||
if value < 256
|
||||
copy value (literal byte) to output stream
|
||||
otherwise
|
||||
if value = end of block (256)
|
||||
break from loop
|
||||
otherwise (value = 257..285)
|
||||
decode distance from input stream
|
||||
|
||||
move backwards distance bytes in the output
|
||||
stream, and copy length bytes from this
|
||||
position to the output stream.
|
||||
*/
|
||||
var numSymbols = 0, blockSize = 0;
|
||||
for (;;) {
|
||||
var symbol = decodeSymbol(bstream, hcLiteralTable);
|
||||
++numSymbols;
|
||||
if (symbol < 256) {
|
||||
// copy literal byte to output
|
||||
buffer.insertByte(symbol);
|
||||
blockSize++;
|
||||
}
|
||||
else {
|
||||
// end of block reached
|
||||
if (symbol === 256) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
var lengthLookup = LengthLookupTable[symbol-257],
|
||||
length = lengthLookup[1] + bstream.readBits(lengthLookup[0]),
|
||||
distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)],
|
||||
distance = distLookup[1] + bstream.readBits(distLookup[0]);
|
||||
|
||||
// now apply length and distance appropriately and copy to output
|
||||
|
||||
// TODO: check that backward distance < data.length?
|
||||
|
||||
// http://tools.ietf.org/html/rfc1951#page-11
|
||||
// "Note also that the referenced string may overlap the current
|
||||
// position; for example, if the last 2 bytes decoded have values
|
||||
// X and Y, a string reference with <length = 5, distance = 2>
|
||||
// adds X,Y,X,Y,X to the output stream."
|
||||
//
|
||||
// loop for each character
|
||||
var ch = buffer.ptr - distance;
|
||||
blockSize += length;
|
||||
if(length > distance) {
|
||||
var data = buffer.data;
|
||||
while (length--) {
|
||||
buffer.insertByte(data[ch++]);
|
||||
}
|
||||
} else {
|
||||
buffer.insertBytes(buffer.data.subarray(ch, ch + length))
|
||||
}
|
||||
|
||||
} // length-distance pair
|
||||
} // length-distance pair or end-of-block
|
||||
} // loop until we reach end of block
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
// {Uint8Array} compressedData A Uint8Array of the compressed file data.
|
||||
// compression method 8
|
||||
// deflate: http://tools.ietf.org/html/rfc1951
|
||||
function inflate(compressedData, numDecompressedBytes) {
|
||||
// Bit stream representing the compressed data.
|
||||
var bstream = new bitjs.io.BitStream(compressedData.buffer,
|
||||
false /* rtl */,
|
||||
compressedData.byteOffset,
|
||||
compressedData.byteLength);
|
||||
var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes);
|
||||
var numBlocks = 0;
|
||||
var blockSize = 0;
|
||||
|
||||
// block format: http://tools.ietf.org/html/rfc1951#page-9
|
||||
do {
|
||||
var bFinal = bstream.readBits(1);
|
||||
var bType = bstream.readBits(2);
|
||||
blockSize = 0;
|
||||
++numBlocks;
|
||||
// no compression
|
||||
if (bType == 0) {
|
||||
// skip remaining bits in this byte
|
||||
while (bstream.bitPtr != 0) bstream.readBits(1);
|
||||
var len = bstream.readBits(16),
|
||||
nlen = bstream.readBits(16);
|
||||
// TODO: check if nlen is the ones-complement of len?
|
||||
|
||||
if(len > 0) buffer.insertBytes(bstream.readBytes(len));
|
||||
blockSize = len;
|
||||
}
|
||||
// fixed Huffman codes
|
||||
else if(bType === 1) {
|
||||
blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer);
|
||||
}
|
||||
// dynamic Huffman codes
|
||||
else if(bType === 2) {
|
||||
var numLiteralLengthCodes = bstream.readBits(5) + 257;
|
||||
var numDistanceCodes = bstream.readBits(5) + 1,
|
||||
numCodeLengthCodes = bstream.readBits(4) + 4;
|
||||
|
||||
// populate the array of code length codes (first de-compaction)
|
||||
var codeLengthsCodeLengths = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
for (var i = 0; i < numCodeLengthCodes; ++i) {
|
||||
codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3);
|
||||
}
|
||||
|
||||
// get the Huffman Codes for the code lengths
|
||||
var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths);
|
||||
|
||||
// now follow this mapping
|
||||
/*
|
||||
0 - 15: Represent code lengths of 0 - 15
|
||||
16: Copy the previous code length 3 - 6 times.
|
||||
The next 2 bits indicate repeat length
|
||||
(0 = 3, ... , 3 = 6)
|
||||
Example: Codes 8, 16 (+2 bits 11),
|
||||
16 (+2 bits 10) will expand to
|
||||
12 code lengths of 8 (1 + 6 + 5)
|
||||
17: Repeat a code length of 0 for 3 - 10 times.
|
||||
(3 bits of length)
|
||||
18: Repeat a code length of 0 for 11 - 138 times
|
||||
(7 bits of length)
|
||||
*/
|
||||
// to generate the true code lengths of the Huffman Codes for the literal
|
||||
// and distance tables together
|
||||
var literalCodeLengths = [];
|
||||
var prevCodeLength = 0;
|
||||
while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) {
|
||||
var symbol = decodeSymbol(bstream, codeLengthsCodes);
|
||||
if (symbol <= 15) {
|
||||
literalCodeLengths.push(symbol);
|
||||
prevCodeLength = symbol;
|
||||
}
|
||||
else if (symbol === 16) {
|
||||
var repeat = bstream.readBits(2) + 3;
|
||||
while (repeat--) {
|
||||
literalCodeLengths.push(prevCodeLength);
|
||||
}
|
||||
}
|
||||
else if (symbol === 17) {
|
||||
var repeat = bstream.readBits(3) + 3;
|
||||
while (repeat--) {
|
||||
literalCodeLengths.push(0);
|
||||
}
|
||||
}
|
||||
else if (symbol === 18) {
|
||||
var repeat = bstream.readBits(7) + 11;
|
||||
while (repeat--) {
|
||||
literalCodeLengths.push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now split the distance code lengths out of the literal code array
|
||||
var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes);
|
||||
|
||||
// now generate the true Huffman Code tables using these code lengths
|
||||
var hcLiteralTable = getHuffmanCodes(literalCodeLengths),
|
||||
hcDistanceTable = getHuffmanCodes(distanceCodeLengths);
|
||||
blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer);
|
||||
}
|
||||
// error
|
||||
else {
|
||||
err("Error! Encountered deflate block of type 3");
|
||||
return null;
|
||||
}
|
||||
|
||||
// update progress
|
||||
currentBytesUnarchivedInFile += blockSize;
|
||||
currentBytesUnarchived += blockSize;
|
||||
postProgress();
|
||||
|
||||
} while (bFinal !== 1);
|
||||
// we are done reading blocks if the bFinal bit was set for this block
|
||||
|
||||
// return the buffer data bytes
|
||||
return buffer.data;
|
||||
}
|
||||
|
||||
// event.data.file has the ArrayBuffer.
|
||||
onmessage = function(event) {
|
||||
unzip(event.data.file, true);
|
||||
};
|
@ -39,7 +39,7 @@
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
@ -64,9 +64,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if other_books is not none %}
|
||||
{% if other_books %}
|
||||
<div class="discover">
|
||||
<h3>{{_("More by")}} {{ author.name|safe }}</h3>
|
||||
<h3>{{_("More by")}} {{ author.name.replace('|',',')|safe }}</h3>
|
||||
<div class="row">
|
||||
{% for entry in other_books %}
|
||||
<div class="col-sm-3 col-lg-2 col-xs-6 book">
|
||||
@ -80,7 +80,7 @@
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="https://www.goodreads.com/author/show/{{ author.gid }}" target="_blank" rel="noopener">
|
||||
{{author.name}}
|
||||
{{author.name.replace('|',',')}}
|
||||
</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
|
@ -1,7 +1,7 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% if book %}
|
||||
<form role="form" action="{{ url_for('edit_book', book_id=book.id) }}" method="post">
|
||||
<form role="form" action="{{ url_for('edit_book', book_id=book.id) }}" method="post" enctype="multipart/form-data">
|
||||
|
||||
<div class="col-sm-3 col-lg-3 col-xs-12">
|
||||
<div class="cover">
|
||||
@ -115,7 +115,15 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if g.user.role_upload() or g.user.role_admin()%}
|
||||
{% if g.allow_upload %}
|
||||
<div role="group" aria-label="Upload new book format">
|
||||
<label class="btn btn-default btn-file" for="btn-upload-format">{{ _('Upload format') }}</label>
|
||||
<div class="upload-format-input-text" id="upload-format"></div>
|
||||
<input id="btn-upload-format" name="btn-upload-format" type="file">
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
|
@ -22,7 +22,7 @@
|
||||
</button>
|
||||
{% for format in entry.data %}
|
||||
<a href="{{ url_for('get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format) }}" id="btnGroupDrop1{{format.format|lower}}" class="btn btn-primary" role="button">
|
||||
<span class="glyphicon glyphicon-download"></span>{{format.format}}
|
||||
<span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }})
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
@ -32,7 +32,7 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
|
||||
{% for format in entry.data %}
|
||||
<li><a href="{{ url_for('get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format) }}">{{format.format}}</a></li>
|
||||
<li><a href="{{ url_for('get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format) }}">{{format.format}} ({{ format.uncompressed_size|filesizeformat }})</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
@ -62,7 +62,7 @@
|
||||
<h2>{{entry.title}}</h2>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', book_id=author.id ) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', book_id=author.id ) }}">{{author.name.replace('|',',')}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save settings')}}</button>
|
||||
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save settings and send Test E-Mail')}}</button>
|
||||
<a href="{{ url_for('admin') }}" class="btn btn-default">{{_('Back')}}</a>
|
||||
<a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -64,8 +64,7 @@
|
||||
<entry>
|
||||
<title>{{entry.name}}</title>
|
||||
<id>{{ url_for(folder, book_id=entry.id) }}</id>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for(folder, book_id=entry.id)}}"/>
|
||||
<link type="application/atom+xml" href="{{url_for(folder, book_id=entry.id)}}" rel="subsection"/>
|
||||
<link rel="subsection" type="application/atom+xml;profile=opds-catalog" href="{{url_for(folder, book_id=entry.id)}}"/>
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<div class="row">
|
||||
|
||||
{% for entry in random %}
|
||||
<div class="col-sm-3 col-lg-2 col-xs-6 book">
|
||||
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books_rand">
|
||||
<div class="cover">
|
||||
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||
{% if entry.has_cover %}
|
||||
@ -42,7 +42,7 @@
|
||||
<div class="row">
|
||||
{% if entries[0] %}
|
||||
{% for entry in entries %}
|
||||
<div class="col-sm-3 col-lg-2 col-xs-6 book">
|
||||
<div class="col-sm-3 col-lg-2 col-xs-6 book" id="books">
|
||||
<div class="cover">
|
||||
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
|
||||
{% if entry.has_cover %}
|
||||
@ -56,7 +56,7 @@
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -12,64 +12,57 @@
|
||||
</author>
|
||||
<entry>
|
||||
<title>{{_('Hot Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_hot')}}" />
|
||||
<link type="application/atom+xml" href="{{url_for('feed_hot')}}" rel="http://opds-spec.org/sort/popular"/>
|
||||
<link rel="http://opds-spec.org/sort/popular" href="{{url_for('feed_hot')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_hot')}}</id>
|
||||
<content type="text">{{_('Popular publications from this catalog based on Downloads.')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('Best rated Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_best_rated')}}" />
|
||||
<link type="application/atom+xml" href="{{url_for('feed_best_rated')}}" rel="http://opds-spec.org/recommended"/>
|
||||
<link rel="http://opds-spec.org/recommended" href="{{url_for('feed_best_rated')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_best_rated')}}</id>
|
||||
<content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('New Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_new')}}" />
|
||||
<link rel="http://opds-spec.org/sort/new" href="{{url_for('feed_new')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
|
||||
<link rel="http://opds-spec.org/sort/new" href="{{url_for('feed_new')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_new')}}</id>
|
||||
<content type="text">{{_('The latest Books')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('Random Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_discover')}}"/>
|
||||
<link rel="http://opds-spec.org/featured" href="{{url_for('feed_discover')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
|
||||
<link rel="http://opds-spec.org/featured" href="{{url_for('feed_discover')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_discover')}}</id>
|
||||
<content type="text">{{_('Show Random Books')}}</content>
|
||||
</entry>
|
||||
{% if not current_user.is_anonymous() %}
|
||||
<entry>
|
||||
<title>{{_('Read Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_read_books')}}" />
|
||||
<link rel="subsection" href="{{url_for('feed_read_books')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
|
||||
<link rel="subsection" href="{{url_for('feed_read_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_read_books')}}</id>
|
||||
<content type="text">{{_('Read Books')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('Unread Books')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_unread_books')}}" />
|
||||
<link rel="subsection" href="{{url_for('feed_unread_books')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
|
||||
<link rel="subsection" href="{{url_for('feed_unread_books')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_unread_books')}}</id>
|
||||
<content type="text">{{_('Unread Books')}}</content>
|
||||
</entry>
|
||||
{% endif %}
|
||||
<entry>
|
||||
<title>{{_('Authors')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_authorindex')}}"/>
|
||||
<link rel="subsection" href="{{url_for('feed_authorindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
<link rel="subsection" href="{{url_for('feed_authorindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_authorindex')}}</id>
|
||||
<content type="text">{{_('Books ordered by Author')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('Category list')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_categoryindex')}}"/>
|
||||
<link rel="subsection" href="{{url_for('feed_categoryindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
<link rel="subsection" href="{{url_for('feed_categoryindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_categoryindex')}}</id>
|
||||
<content type="text">{{_('Books ordered by category')}}</content>
|
||||
</entry>
|
||||
<entry>
|
||||
<title>{{_('Series list')}}</title>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_seriesindex')}}"/>
|
||||
<link rel="subsection" href="{{url_for('feed_seriesindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
|
||||
<link rel="subsection" href="{{url_for('feed_seriesindex')}}" type="application/atom+xml;profile=opds-catalog"/>
|
||||
<id>{{url_for('feed_seriesindex')}}</id>
|
||||
<content type="text">{{_('Books ordered by series')}}</content>
|
||||
</entry>
|
||||
|
@ -41,7 +41,7 @@
|
||||
"rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
|
||||
"authors": [
|
||||
{% for author in entry.authors %}
|
||||
"{{author.name}}"{% if not loop.last %},{% endif %}
|
||||
"{{author.name.replace('|',',')}}"{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
],
|
||||
"other_formats": {
|
||||
@ -51,4 +51,4 @@
|
||||
{% endfor %}
|
||||
{% endif %} },
|
||||
"title_sort": "{{entry.sort}}"
|
||||
}
|
||||
}
|
||||
|
@ -32,9 +32,9 @@
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="{{url_for('index')}}">Calibre Web</a>
|
||||
<a class="navbar-brand" href="{{url_for('index')}}">{{instance}}</a>
|
||||
</div>
|
||||
{% if g.user.is_authenticated or g.user.is_anonymous() %}
|
||||
{% if g.user.is_authenticated() or g.user.is_anonymous() %}
|
||||
<form class="navbar-form navbar-left" role="search" action="{{url_for('search')}}" method="GET">
|
||||
<div class="form-group input-group input-group-sm">
|
||||
<label for="query" class="sr-only">{{_('Search')}}</label>
|
||||
@ -46,13 +46,13 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="navbar-collapse collapse">
|
||||
{% if g.user.is_authenticated or g.user.is_anonymous() %}
|
||||
{% if g.user.is_authenticated() or g.user.is_anonymous() %}
|
||||
<ul class="nav navbar-nav ">
|
||||
<li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span><span class="hidden-sm"> {{_('Advanced Search')}}</span></a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<ul class="nav navbar-nav navbar-right" id="main-nav">
|
||||
{% if g.user.is_authenticated or g.user.is_anonymous() %}
|
||||
{% if g.user.is_authenticated() or g.user.is_anonymous() %}
|
||||
{% if g.user.role_upload() or g.user.role_admin()%}
|
||||
{% if g.allow_upload %}
|
||||
<li>
|
||||
@ -72,8 +72,10 @@
|
||||
<li><a id="logout" href="{{url_for('logout')}}"><span class="glyphicon glyphicon-log-out"></span><span class="hidden-sm"> {{_('Logout')}}</span></a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if g.allow_registration and not g.user.is_authenticated %}
|
||||
{% if not g.user.is_authenticated() %}
|
||||
<li><a id="login" href="{{url_for('login')}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li>
|
||||
{% endif %}
|
||||
{% if g.allow_registration and not g.user.is_authenticated() %}
|
||||
<li><a id="register" href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
@ -99,7 +101,7 @@
|
||||
{% endfor %}
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
{% if g.user.is_authenticated or g.user.is_anonymous() %}
|
||||
{% if g.user.is_authenticated() or g.user.is_anonymous() %}
|
||||
<div class="col-sm-2">
|
||||
<nav class="navigation">
|
||||
<ul class="list-unstyled" id="scnd-nav" intent in-standard-append="nav.navigation" in-mobile-after="#main-nav" in-mobile-class="nav navbar-nav">
|
||||
@ -140,7 +142,7 @@
|
||||
{% if g.user.filter_language() == 'all' and g.user.show_language() %}
|
||||
<li id="nav_lang"><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li>
|
||||
{%endif%}
|
||||
{% if g.user.is_authenticated or g.user.is_anonymous() %}
|
||||
{% if g.user.is_authenticated() or g.user.is_anonymous() %}
|
||||
<li class="nav-head hidden-xs">{{_('Public Shelves')}}</li>
|
||||
{% for shelf in g.public_shelfes %}
|
||||
<li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span> {{shelf.name}}</a></li>
|
||||
|
@ -11,59 +11,6 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/libs/normalize.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/popup.css') }}">
|
||||
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/zip.min.js') }}"></script>
|
||||
|
||||
<!-- Full Screen -->
|
||||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||||
|
||||
<!-- Render -->
|
||||
<script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
||||
|
||||
<!-- Hooks -->
|
||||
<script src="{{ url_for('static', filename='js/libs/hooks.min.js') }}"></script>
|
||||
|
||||
<!-- Reader -->
|
||||
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState == "complete") {
|
||||
EPUBJS.filePath = "{{ url_for('static', filename='js/libs/') }}";
|
||||
EPUBJS.cssPath = "{{ url_for('static', filename='css/') }}";
|
||||
|
||||
window.reader = ePubReader("{{ url_for('static', filename=bookid) }}/");
|
||||
//keybind
|
||||
/*$(document).keydown(function(event){
|
||||
if(event.keyCode == 37){
|
||||
//window.reader.book.prevPage();
|
||||
event.preventDefault();
|
||||
}
|
||||
if(event.keyCode == 39){
|
||||
//swindow.reader.book.nextPage();
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
//bind mouse
|
||||
$(window).bind('DOMMouseScroll mousewheel', function(event) {
|
||||
var delta = 0;
|
||||
if (event.originalEvent.wheelDelta) {
|
||||
delta = event.originalEvent.wheelDelta;
|
||||
}else if (event.originalEvent.detail) {
|
||||
delta = event.originalEvent.detail*-1;
|
||||
}
|
||||
if (delta >= 0) {
|
||||
window.reader.book.prevPage();
|
||||
}
|
||||
else {
|
||||
window.reader.book.nextPage();
|
||||
}
|
||||
});*/
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="sidebar">
|
||||
@ -73,24 +20,24 @@
|
||||
<!--a id="show-Search" class="show_view icon-search" data-view="Search">Search</a-->
|
||||
<a id="show-Toc" class="show_view icon-list-1 active" data-view="Toc">TOC</a>
|
||||
<a id="show-Bookmarks" class="show_view icon-bookmark" data-view="Bookmarks">Bookmarks</a>
|
||||
<a id="show-Notes" class="show_view icon-edit" data-view="Notes">Notes</a>
|
||||
<!--a id="show-Notes" class="show_view icon-edit" data-view="Notes">Notes</a-->
|
||||
|
||||
</div>
|
||||
<div id="tocView" class="view">
|
||||
</div>
|
||||
<div id="searchView" class="view">
|
||||
<!--div id="searchView" class="view">
|
||||
<ul id="searchResults"></ul>
|
||||
</div>
|
||||
</div-->
|
||||
<div id="bookmarksView" class="view">
|
||||
<ul id="bookmarks"></ul>
|
||||
</div>
|
||||
<div id="notesView" class="view">
|
||||
<!--div id="notesView" class="view">
|
||||
<div id="new-note">
|
||||
<textarea id="note-text"></textarea>
|
||||
<button id="note-anchor">Anchor</button>
|
||||
</div>
|
||||
<ol id="notes"></ol>
|
||||
</div>
|
||||
</div-->
|
||||
</div>
|
||||
<div id="main">
|
||||
|
||||
@ -119,7 +66,7 @@
|
||||
</div>
|
||||
<div class="modal md-effect-1" id="settings-modal">
|
||||
<div class="md-content">
|
||||
<h3>Settings</h3>
|
||||
<h3>{{_('Settings')}}</h3>
|
||||
<div>
|
||||
<p>
|
||||
<input type="checkbox" id="sidebarReflow" name="sidebarReflow">{{_('Reflow text when sidebars are open.')}}
|
||||
@ -129,5 +76,23 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.calibre = {
|
||||
filePath: "{{ url_for('static', filename='js/libs/') }}",
|
||||
cssPath: "{{ url_for('static', filename='css/') }}",
|
||||
bookUrl: "{{ url_for('static', filename=bookid) }}/",
|
||||
bookmarkUrl: "{{ url_for('bookmark', book_id=bookid, book_format='EPUB') }}",
|
||||
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
|
||||
useBookmarks: {{ g.user.is_authenticated() | tojson }}
|
||||
};
|
||||
</script>
|
||||
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/zip.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/epub.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/hooks.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/reader.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/reading/epub.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
44
cps/templates/readcbr.html
Normal file
44
cps/templates/readcbr.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>Comic Reader</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/kthoom.css') }}" type="text/css"/>
|
||||
<link href="{{ url_for('static', filename='css/libs/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<script src="{{ url_for('static', filename='js/libs/jquery.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/libs/bootstrap.min.js') }}"></script>
|
||||
|
||||
<script src="{{ url_for('static', filename='js/kthoom.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/archive.js') }}"></script>
|
||||
<script>
|
||||
document.onreadystatechange = function () {
|
||||
if (document.readyState == "complete") {
|
||||
init("{{ url_for('static', filename=comicfile) }}");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main" id="main">
|
||||
<div id="titlebar">
|
||||
<div id="progress" class="hide"></div>
|
||||
<div class="progress2" style="display:none">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="70"
|
||||
aria-valuemin="0" aria-valuemax="100" style="width:70%">
|
||||
70%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mainContent">
|
||||
<div id="mainText" style="display:none"></div>
|
||||
<canvas id="mainImage"></canvas>
|
||||
</div>
|
||||
<div id="prev" class="arrow" onclick="showPrevPage()">‹</div>
|
||||
<div id="next" class="arrow" onclick="showNextPage()">›</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% if entries|length < 1 %}
|
||||
<h2>{{_('No Results for:')}} {{searchterm}}</h2>
|
||||
<p>{{_('Please try a diffrent Search')}}</p>
|
||||
<p>{{_('Please try a different search')}}</p>
|
||||
{% else %}
|
||||
<h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2>
|
||||
{%endif%}
|
||||
@ -24,7 +24,7 @@
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', book_id=author.id ) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', book_id=author.id ) }}">{{author.name.replace('|',',')}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -2,7 +2,7 @@
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h2>{{title}}</h2>
|
||||
{% if g.user.is_authenticated %}
|
||||
{% if g.user.is_authenticated() %}
|
||||
{% if (g.user.role_edit_shelfs() and shelf.is_public ) or not shelf.is_public %}
|
||||
<div data-toggle="modal" data-target="#DeleteShelfDialog" class="btn btn-danger">{{ _('Delete this Shelf') }} </div>
|
||||
<a href="{{ url_for('edit_shelf', shelf_id=shelf.id) }}" class="btn btn-primary">{{ _('Edit Shelf name') }} </a>
|
||||
@ -24,7 +24,7 @@
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', book_id=author.id) }}">{{author.name.replace('|',',')}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -1,5 +1,9 @@
|
||||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<h3>{{_('About')}}</h3>
|
||||
<p>{{instance}} powered by
|
||||
<a href="https://github.com/janeczku/calibre-web" title="Calibre-Web">Calibre Web</a>.
|
||||
</p>
|
||||
<h3>{{_('Calibre library statistics')}}</h3>
|
||||
<table id="stats" class="table">
|
||||
<tbody>
|
||||
|
@ -41,10 +41,6 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_mature_content" id="show_mature_content" {% if content.mature_content %}checked{% endif %}>
|
||||
<label for="show_mature_content">{{_('Show mature content')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_random" id="show_random" {% if content.show_random_books() %}checked{% endif %}>
|
||||
<label for="show_random">{{_('Show random books')}}</label>
|
||||
@ -88,6 +84,10 @@
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
|
||||
<label for="admin_role">{{_('Admin user')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_mature_content" id="show_mature_content" {% if content.mature_content %}checked{% endif %}>
|
||||
<label for="show_mature_content">{{_('Show mature content')}}</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="form-group">
|
||||
|
Binary file not shown.
@ -21,7 +21,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-web\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/janeczku/calibre-web\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:19+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2016-07-12 19:54+0200\n"
|
||||
"Last-Translator: Ozzie Isaacs\n"
|
||||
"Language: de\n"
|
||||
@ -32,7 +32,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "Nicht installiert"
|
||||
|
||||
@ -78,379 +78,379 @@ msgstr ""
|
||||
"Konnte keine Formate finden welche für das versenden per E-Mail geeignet "
|
||||
"sind"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "Gast"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "Frage Update Paket an"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "Lade Update Paket herunter"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Entpacke Update Paket"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "Ersetze Dateien"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Schließe Datenbankverbindungen"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "Stoppe Server"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Update abgeschlossen, bitte okay drücken und Seite neu laden"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr "Kürzlich hinzugefügte Bücher"
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr "Neueste Bücher"
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr "Älteste Bücher"
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr "Bücher (A-Z)"
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr "Bücher (Z-A)"
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "Beliebte Bücher (die meisten Downloads)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "Best bewertete Bücher"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "Zufällige Bücher"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "Autorenliste"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr ""
|
||||
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
|
||||
"zugänglich."
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "Liste Serien"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "Serie: %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "Verfügbare Sprachen"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "Sprache: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "Kategorieliste"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "Kategorie: %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr "Ausführungsberechtigung nicht vorhanden"
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "Statistiken"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "Server neu gestartet,bitte Seite neu laden"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "Server wird runtergefahren, bitte Fenster schließen"
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "Update durchgeführt"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "Gelesene Bücher"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "Ungelesene Bücher"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "Lese ein Buch"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2512
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "Bitte alle Felder ausfüllen!"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "Registieren"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen."
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung."
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "Du bist nun eingeloggt als '%(nickname)s'"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "Falscher Benutzername oder Passwort"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "Login"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr "Token wurde nicht gefunden"
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr "Das Token ist abgelaufen"
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr "Erfolg! Bitte zum Gerät zurückkehren"
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..."
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "Buch erfolgreich versandt an %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "Beim Senden des Buchs trat ein Fehler auf: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2597
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'"
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "Bücherregal %(title)s erzeugt"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "Es trat ein Fehler auf"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "Bücherregal erzeugen"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "Bücherregal %(title)s verändert"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "Bücherregal editieren"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "Bücherregal %(name)s erfolgreich gelöscht"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "Bücherregal: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr "Fehler beim Öffnen. Bücherregel exisitert nicht oder ist nicht zugänglich"
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "Reihenfolge in Bücherregal '%(name)s' verändern"
|
||||
|
||||
#: cps/web.py:2325
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "Es existiert ein Benutzerkonto für diese E-Mailadresse"
|
||||
|
||||
#: cps/web.py:2327 cps/web.py:2331
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "%(name)s's Profil"
|
||||
|
||||
#: cps/web.py:2328
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "Profil aktualisiert"
|
||||
|
||||
#: cps/web.py:2342
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "Admin Seite"
|
||||
|
||||
#: cps/web.py:2466
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Calibre-web Konfiguration wurde aktualisiert"
|
||||
|
||||
#: cps/web.py:2473 cps/web.py:2479 cps/web.py:2493
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "Basis Konfiguration"
|
||||
|
||||
#: cps/web.py:2477
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "DB Speicherort ist ungültig, bitte Pfad korrigieren"
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2514 cps/web.py:2567
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "Neuen Benutzer hinzufügen"
|
||||
|
||||
#: cps/web.py:2559
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Benutzer '%(user)s' angelegt"
|
||||
|
||||
#: cps/web.py:2563
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr ""
|
||||
"Es existiert ein Benutzerkonto für diese Emailadresse oder den "
|
||||
"Benutzernamen."
|
||||
|
||||
#: cps/web.py:2585
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "E-Mail Einstellungen aktualisiert"
|
||||
|
||||
#: cps/web.py:2592
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "Test E-Mail erfolgreich an %(kindlemail)s versendet"
|
||||
|
||||
#: cps/web.py:2595
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "Fehler beim versenden der Test E-Mail: %(res)s"
|
||||
|
||||
#: cps/web.py:2599
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "E-Mail Einstellungen wurde aktualisiert"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "E-Mail Einstellungen editieren"
|
||||
|
||||
#: cps/web.py:2629
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Benutzer '%(nick)s' gelöscht"
|
||||
|
||||
#: cps/web.py:2727
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Benutzer '%(nick)s' aktualisiert"
|
||||
|
||||
#: cps/web.py:2730
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "Es ist ein unbekanter Fehler aufgetreten"
|
||||
|
||||
#: cps/web.py:2733
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "Benutzer %(nick)s bearbeiten"
|
||||
|
||||
#: cps/web.py:2755
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
|
||||
"zugänglich"
|
||||
|
||||
#: cps/web.py:2770 cps/web.py:2953 cps/web.py:3077
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "Metadaten editieren"
|
||||
|
||||
#: cps/web.py:2782 cps/web.py:2786
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr "Unbekannt"
|
||||
|
||||
#: cps/web.py:2971
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "Die Dateiendung \"%s\" kann nicht auf diesen Server hochgeladen werden"
|
||||
|
||||
#: cps/web.py:2977
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "Datei müssen eine Erweiterung haben, um hochgeladen zu werden"
|
||||
|
||||
#: cps/web.py:2996
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)"
|
||||
|
||||
#: cps/web.py:3001
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)"
|
||||
|
||||
#: cps/web.py:3006
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "Fehler beim Löschen von Datei %s (Zugriff verweigert)"
|
||||
@ -621,6 +621,18 @@ msgstr "Calibre-web wirklich stoppen"
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "Updatevorgang, bitte Seite nicht neu laden"
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr "via"
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr "In Bibliothek"
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr "Mehr von"
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr "Buch löschen"
|
||||
@ -629,12 +641,13 @@ msgstr "Buch löschen"
|
||||
msgid "Book Title"
|
||||
msgstr "Buchtitel"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "Autor"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
@ -699,7 +712,7 @@ msgstr "Das Buch wird aus der Calibre Datenbank"
|
||||
|
||||
#: cps/templates/book_edit.html:144
|
||||
msgid "and from hard disk"
|
||||
msgstr "und der Festplatte gelöscht"
|
||||
msgstr "und von der Festplatte gelöscht"
|
||||
|
||||
#: cps/templates/book_edit.html:148
|
||||
msgid "Delete"
|
||||
@ -717,35 +730,35 @@ msgstr "Suchbegriff"
|
||||
msgid "Go!"
|
||||
msgstr "Los!"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "Klicke auf das Bild um die Metadaten zu übertragen"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "Lade..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "Schließen"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "Fehler bei Suche!"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Kein Ergebniss! Bitte anderen Begriff versuchen"
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "Herausgeber"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "Quelle"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "Fehler bei Suche!"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Kein Ergebniss! Bitte anderen Begriff versuchen"
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Speicherort der Calibre Datenbank"
|
||||
@ -829,13 +842,13 @@ msgstr "Öffentlicher Goodreads API Schlüssel"
|
||||
|
||||
#: cps/templates/config_edit.html:119
|
||||
msgid "Goodreads API Secret"
|
||||
msgstr Geheimer Goodreads API Schlüssel"
|
||||
msgstr "eheimer Goodreads API Schlüssel"
|
||||
|
||||
#: cps/templates/config_edit.html:125
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "Default Einstellungen für neue Benutzer"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "Admin Benutzer"
|
||||
|
||||
@ -938,6 +951,11 @@ msgstr "Einstellungen speichern und Test E-Mail versenden"
|
||||
msgid "Next"
|
||||
msgstr "Nächste"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "Entdecke (Zufälliges Buch)"
|
||||
@ -946,52 +964,47 @@ msgstr "Entdecke (Zufälliges Buch)"
|
||||
msgid "Start"
|
||||
msgstr "Start"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Suche"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "Beliebte Bücher"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "Beliebte Publikationen aus dieser Bibliothek basierend auf Downloadzahlen"
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "Best bewertete Bücher"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "Beliebte Veröffentlichungen dieses Katalogs basierend auf Bewertungen"
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "Neue Bücher"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "Die neuesten Bücher"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "Zeige zufällige Bücher"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "Autoren"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "Bücher nach Autoren sortiert"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "Bücher nach Kategorien sortiert"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "Bücher nach Reihen geordnet"
|
||||
|
||||
@ -1102,7 +1115,11 @@ msgstr "Einloggen mit magischem Link"
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr "Calibre Web Ebook Katalog"
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr "Text umbrechen wenn Seitenleiste geöffnet ist"
|
||||
|
||||
@ -1151,7 +1168,7 @@ msgid "No Results for:"
|
||||
msgstr "Keine Ergebnisse für:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr "Versuche eine andere Suche"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1243,45 +1260,45 @@ msgid "Show all"
|
||||
msgstr "Zeige alle"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr "Erwachsenencontent anzeigen"
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "Zeige Zufällige Bücher"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "Zeige Auswahl Beliebte Bücher"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "Zeige am besten bewertete Bücher"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "Zeige Sprachauswahl"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "Zeige Serienauswahl"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "Zeige Kategorienauswahl"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "Zeige Autorenauswahl"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "Zeige Gelesen/Ungelesen Auswahl"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "Zeige zufällige Bücher in der Detailansicht"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr "Erwachsenencontent anzeigen"
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "Benutzer löschen"
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-web\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/janeczku/calibre-web\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2017-04-04 15:09+0200\n"
|
||||
"Last-Translator: Juan F. Villa <juan.villa@paisdelconocimiento.org>\n"
|
||||
"Language: es\n"
|
||||
@ -25,7 +25,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "No instalado"
|
||||
|
||||
@ -69,375 +69,375 @@ msgstr "Enviar a Kindle"
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr "Formato no compatible para enviar por correo electronico"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "Invitado"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "Solicitando paquete de actualización"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "Descargando paquete de actualización"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Descomprimiendo paquete de actualización"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "Ficheros sustituidos"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Los conexiones de base datos están cerradas"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "El servidor está detenido"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Actualización finalizada. Por favor, pulse OK y recargue la página"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "Libros populares (los mas descargados)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "Libros mejor valorados"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "Libros al azar"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "Lista de autores"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr "Error en la apertura del eBook. El archivo no existe o no es accesible:"
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "Lista de series"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "Series : %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "Lenguajes disponibles"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "Lenguaje: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "Lista de categorias"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "Categoría : %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "Estadisticas"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "Servidor reiniciado. Por favor, recargue la página"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "Servidor en proceso de apagado. Por favor, cierre la ventana."
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "Actualización realizada"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "búsqueda"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "Libros leídos"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "Libros no leídos"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "Leer un libro"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "¡Por favor completar todos los campos!"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "registrarse"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "Error desconocido. Por favor, inténtelo de nuevo mas tarde."
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "Usuario o dirección de correo en uso."
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "Sesion iniciada como : '%(nickname)s'"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "Usuario o contraseña invalido"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "Iniciar sesión"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "Configurar primero los parametros SMTP por favor..."
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "Envio de Libro a %(kindlemail)s correctamente"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "Ha sucedido un error en el envio del Libro: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "Configurar primero la dirección de correo Kindle por favor..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "El libro fue agregado a el estante: %(sname)s"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "El libro fue removido del estante: %(sname)s"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "Une étagère de ce nom '%(title)s' existe déjà."
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "Estante %(title)s creado"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "Ha sucedido un error"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "crear un estante"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "Estante %(title)s cambiado"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "Editar un estante"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "Estante %(name)s fue borrado correctamente"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "Estante: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "Cambiar orden del estante: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "Existe una cuenta vinculada a esta dirección de correo."
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "Perfil de %(name)s"
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "Perfil actualizado"
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "Página de administración"
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Configuración de Calibre-web actualizada"
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "Configuración básica"
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "Localicación de la BD inválida. Por favor, introduzca la ruta correcta."
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "Agregar un nuevo usuario"
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Usuario '%(user)s' creado"
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr ""
|
||||
"Se ha encontrado una cuenta vinculada a esta dirección de correo o nombre"
|
||||
" de usuario."
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "Parámetros de correo actualizados"
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "Exito al realizar envio de prueba a %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "Error al realizar envio de prueba a E-Mail: %(res)s"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "Ajustes de correo electrónico actualizados"
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "Editar parametros de correo"
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Usuario '%(nick)s' borrado"
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Usuario '%(nick)s' actualizado"
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "Error inesperado."
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "Editar Usuario %(nick)s"
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "editar metainformación"
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "No se permite subir archivos con la extensión \"%s\" a este servidor"
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "El archivo a subir debe tener una extensión"
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "Fallo al crear la ruta %s (permiso negado)"
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "Fallo al almacenar el archivo %s (permiso negado)"
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "Fallo al borrar el archivo %s (permiso negado)"
|
||||
@ -475,6 +475,10 @@ msgstr "Descarga"
|
||||
msgid "Upload"
|
||||
msgstr "Subir archivo"
|
||||
|
||||
#: cps/templates/detail.html:14
|
||||
msgid "Upload format"
|
||||
msgstr "Subir formato"
|
||||
|
||||
#: cps/templates/admin.html:15
|
||||
msgid "Edit"
|
||||
msgstr "Editar"
|
||||
@ -608,6 +612,18 @@ msgstr "¿Seguro que quiere detener Calibre-web?"
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "Actualizando. Por favor, no recargue la página."
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr ""
|
||||
@ -616,12 +632,13 @@ msgstr ""
|
||||
msgid "Book Title"
|
||||
msgstr "Titulo del Libro"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "Autor"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "Descripcion"
|
||||
|
||||
@ -704,35 +721,35 @@ msgstr "Buscar palabras clave"
|
||||
msgid "Go!"
|
||||
msgstr "¡Vamos!"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "Haga clic en la portada para cargar la metainformación en el formulario"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "Cargando..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "Cerrar"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "¡Error en la búsqueda!"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "¡Sin resultados! Por favor, pruebe otra palabra clave."
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "Editor"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "Origen"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "¡Error en la búsqueda!"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "¡Sin resultados! Por favor, pruebe otra palabra clave."
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Ubicación de la base de datos Calibre"
|
||||
@ -822,7 +839,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "Ajustes por defecto para nuevos usuarios"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "Usuario Administrador"
|
||||
|
||||
@ -925,6 +942,11 @@ msgstr "Guardar cambios y enviar un correo de prueba"
|
||||
msgid "Next"
|
||||
msgstr "Siguiente"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "Descubrir (Libros al azar)"
|
||||
@ -933,52 +955,47 @@ msgstr "Descubrir (Libros al azar)"
|
||||
msgid "Start"
|
||||
msgstr "Iniciar"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Buscar"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "Libros Populares"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "Publicaciones mas populares para este catálogo basadas en las descargas."
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "Libros mejor valorados"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "Publicaciones populares del catalogo basados en el puntaje."
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "Nuevos libros"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "Libros recientes"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "Mostrar libros al azar"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "Autores"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "Libros ordenados por Autor"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "Libros ordenados por Categorias"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "Libros ordenados por Series"
|
||||
|
||||
@ -1089,7 +1106,11 @@ msgstr ""
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr "Catálogo de libros electrónicos Calibre Web"
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr "Redimensionar el texto cuando las barras laterales estan abiertas"
|
||||
|
||||
@ -1138,7 +1159,7 @@ msgid "No Results for:"
|
||||
msgstr "Sin resultados para:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr "Intente una busqueda diferente"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1230,45 +1251,45 @@ msgid "Show all"
|
||||
msgstr "Mostrar Todo"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "Mostrar libros al azar"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "Mostrar libros populares"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "Mostrar libros mejor valorados"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "Mostrar lenguaje seleccionado"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "Mostrar series seleccionadas"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "Mostrar categorias elegidas"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "Mostrar selección de autores"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "Mostrar leídos y no leídos"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "Mostrar libro aleatorios con vista detallada"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "Borrar este usuario"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
cps/translations/it/LC_MESSAGES/messages.mo
Normal file
BIN
cps/translations/it/LC_MESSAGES/messages.mo
Normal file
Binary file not shown.
24787
cps/translations/it/LC_MESSAGES/messages.po
Normal file
24787
cps/translations/it/LC_MESSAGES/messages.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -22,7 +22,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-web dutch translation by Ed Driesen (GPL V3)\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2017-06-21 20:15+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language: nl\n"
|
||||
@ -33,7 +33,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "niet geïnstalleerd"
|
||||
|
||||
@ -77,382 +77,384 @@ msgstr "Stuur naar Kindle:"
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr "Kon geen geschikte formaten vinden om te verzenden per email"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "Gast"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "Update pakket wordt aangevraagd"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "Update pakket wordt gedownload"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Update pakket wordt uitgepakt"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "Bestanden zijn vervangen"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Database verbindingen zijn gesloten"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "Server is gestopt"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Update voltooid, klik op ok en herlaad de pagina"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr ""
|
||||
msgstr "Recent toegevoegde boeken"
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr ""
|
||||
msgstr "Nieuwste boeken"
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr ""
|
||||
msgstr "Oudste boeken"
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr ""
|
||||
msgstr "Boeken (A-Z)"
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr ""
|
||||
msgstr "Boeken (A-Z)"
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "Populaire boeken (meeste downloads)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "Best beoordeelde boeken"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "Willekeurige boeken"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "Auteur lijst"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr ""
|
||||
"Fout bij openen van het boek. Bestand bestaat niet of is niet "
|
||||
"toegankelijk:"
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "Series lijst"
|
||||
msgstr "Serie lijst"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "Series: %(serie)s"
|
||||
msgstr "Serie: %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "Beschikbare talen"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "Taal: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "Categorie lijst"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "Categorie: %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "Statistieken"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "Server herstart, gelieve de pagina herladen"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "Bezig met het stoppen vande server, gelieve venster afsluiten"
|
||||
msgstr "Bezig met het stoppen van de server, gelieve venster te sluiten"
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "Update voltooid"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "zoek"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "Lees Boeken"
|
||||
msgstr "Gelezen Boeken"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "Ongelezen Boeken"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "Lees een boek"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "Gelieve alle velden in te vullen!"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "registreer"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "Een onbekende fout deed zich voor. Gelieve later nog eens te proberen."
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "Deze gebruikersnaam of email adres is reeds in gebruik."
|
||||
msgstr "Deze gebruikersnaam of dit emailadres is reeds in gebruik."
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "je bent nu ingelogd als: '%(nickname)s'"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "Verkeerde gebruikersnaam of Wachtwoord"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "login"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr ""
|
||||
msgstr "Token niet gevonden"
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr ""
|
||||
msgstr "Token is verlopen"
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr ""
|
||||
msgstr "Gelukt! Ga terug naar je apparaat"
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "Gelieve de SMTP mail instellingen eerstte configureren..."
|
||||
msgstr "Gelieve de SMTP mail instellingen eerst te configureren..."
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "Boek met succes verstuurd naar %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "Er trad een fout op bij het versturen van dit boek: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "Gelieve eerst je kindle email adres te configureren..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "Boek werd toegevoegd aan boekenplank: %(sname)s"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "Boek werd verwijderd van boekenplank: %(sname)s"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "Een boekenplank met de naam '%(title)s' bestaat reeds."
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "Boekenplank %(title)s aangemaakt"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "Er deed zich een fout voor"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "maak een boekenplank"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "Boekenplank %(title)s gewijzigd"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "Bewerk een boekenplank"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "Succesvol boekenplank %(name)s gewist"
|
||||
msgstr "Boekenplank %(name)s succesvol gewist"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "Boekenplank: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr ""
|
||||
"Fout bij openen boekenplank. Boekenplank bestaat niet of is niet "
|
||||
"toegankelijk"
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "Verander volgorde van Boekenplank: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "Een bestaand gebruiker gevonden voor dit email adres."
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "%(name)s's profiel"
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "Profiel aangepast"
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "Administratie pagina"
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Calibre-web configuratie aangepast"
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "Basis configuratie"
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "DB locatie is niet geldig, gelieve het correcte pad in te geven"
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "Voeg nieuwe gebruiker toe"
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Gebruiker '%(user)s' aangemaakt"
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr "Een bestaand gebruiker gevonden voor dit email adres of gebruikersnaam."
|
||||
msgstr "Een bestaande gebruiker gevonden voor dit emailadres of gebruikersnaam."
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "Mail instellingen aangepast"
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "Test email met succes verstuurd naar %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "Er trad een fout op met het versturen van de test email: %(res)s"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "Email instellingen aangepast"
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "Bewerk mail instellingen"
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Gebruiker '%(nick)s' verwijderd"
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Gebruiker '%(nick)s' aangepast"
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "Een onbekende fout deed zich voor."
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "Bewerk gebruiker '%(nick)s'"
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
msgstr "Fout bij openen eBook. Het bestand bestaat niet of is niet toegankelijk"
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "Bewerk metadata"
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr "onbekend"
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "Het uploaden van bestandsextensie \"%s\" is niet toegestaan op deze server"
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "Up te loaden bestanden dienen een extentie te hebben"
|
||||
msgstr "Up te loaden bestanden dienen een extensie te hebben"
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "Het pad %s aanmaken gefaald (Geen toestemming)."
|
||||
msgstr "Het pad %s aanmaken mislukt (Geen toestemming)."
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "Bestand %s opslaan gefaald (Geen toestemming)."
|
||||
msgstr "Bestand %s opslaan mislukt (Geen toestemming)."
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "Bestand %s wissen gefaald (Geen toestemming)."
|
||||
msgstr "Bestand %s wissen mislukt (Geen toestemming)."
|
||||
|
||||
#: cps/templates/admin.html:4
|
||||
msgid "User list"
|
||||
msgstr "Gebruikers lijst"
|
||||
msgstr "Gebruikerslijst"
|
||||
|
||||
#: cps/templates/admin.html:8
|
||||
msgid "Nickname"
|
||||
@ -557,7 +559,7 @@ msgstr "Anoniem verkennen"
|
||||
|
||||
#: cps/templates/admin.html:67 cps/templates/remote_login.html:4
|
||||
msgid "Remote Login"
|
||||
msgstr ""
|
||||
msgstr "Login op afstand"
|
||||
|
||||
#: cps/templates/admin.html:80
|
||||
msgid "Administration"
|
||||
@ -616,6 +618,18 @@ msgstr "Wil je Calibre-web echt stoppen?"
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "Aan het updaten, gelieve de pagina niet te herladen"
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr "Wis boek"
|
||||
@ -624,12 +638,13 @@ msgstr "Wis boek"
|
||||
msgid "Book Title"
|
||||
msgstr "Boek titel"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "Auteur"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "Omschrijving"
|
||||
|
||||
@ -712,35 +727,35 @@ msgstr "Zoek voor zoekwoord"
|
||||
msgid "Go!"
|
||||
msgstr "Start!"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "Klik op de omslag om de metatadata in het formulier te laden"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "Aan het laden..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "Sluit"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "Zoek fout!"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Geen resultaat! Gelieve een ander zoekwoord proberen"
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "Uitgever"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "Bron"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "Zoek fout!"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Geen resultaat! Gelieve een ander zoekwoord proberen"
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Locatie van de Calibre database"
|
||||
@ -830,7 +845,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "Standaard instellingen voor nieuwe gebruikers"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "Administratie gebruiker"
|
||||
|
||||
@ -933,6 +948,11 @@ msgstr "Bewaar instellingen en stuur test email"
|
||||
msgid "Next"
|
||||
msgstr "Volgende"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Zoek"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "Ontdek (Willekeurige Boeken)"
|
||||
@ -941,52 +961,47 @@ msgstr "Ontdek (Willekeurige Boeken)"
|
||||
msgid "Start"
|
||||
msgstr "Start"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Zoek"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "Populaire Boeken"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "Populaire publicaties van deze cataloog gebaseerd op Downloads."
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "Best beoordeeld"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "Populaire publicaties van deze cataloog gebaseerd op Beoordeling."
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "Nieuwe Boeken"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "Recentste boeken"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "Toon Willekeurige Boeken"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "Auteurs"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "Boeken gesorteerd op Auteur"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "Boeken gesorteerd op Categorie"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "Boeken gesorteerd op Serie"
|
||||
|
||||
@ -1097,7 +1112,11 @@ msgstr ""
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr "Calible web ebook cataloog"
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr "Herschuif tekst waneer het zijpaneel open staat."
|
||||
|
||||
@ -1146,7 +1165,7 @@ msgid "No Results for:"
|
||||
msgstr "Geen resultaat voor:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr "Gelieve een ander zoekwoord proberen"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1238,45 +1257,45 @@ msgid "Show all"
|
||||
msgstr "Toon alles"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "Toon willekeurige boeken"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "Toon populaire boeken"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "Toon best beoordeelde boeken"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "Toon taal selectie"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "Toon serie selectie"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "Toon categorie selectie"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "Toon auteur selectie"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "Toon gelezen en ongelezen"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "Toon willekeurige boeken in gedetailleerd zicht"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "Wis deze gebruiker"
|
||||
|
Binary file not shown.
@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre Web - polski (POT: 2017-04-11 22:51)\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2017-04-11 22:51+0200\n"
|
||||
"Last-Translator: Radosław Kierznowski <radek.kierznowski@outlook.com>\n"
|
||||
"Language: pl\n"
|
||||
@ -24,7 +24,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "nie zainstalowane"
|
||||
|
||||
@ -70,373 +70,373 @@ msgstr ""
|
||||
"Nie można znaleźć żadnych formatów przystosowane do wysyłania pocztą "
|
||||
"e-mail"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "Gość"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "Żądanie o pakiet aktualizacji"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "Pobieranie pakietu aktualizacji"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Rozpakowywanie pakietu aktualizacji"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "Pliki zostały zastąpione"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Połączenia z bazą danych zostały zakończone"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "Serwer jest zatrzymany"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Aktualizacja zakończona, proszę nacisnąć OK i odświeżyć stronę"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "Najpopularniejsze książki (najczęściej pobierane)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "Najlepiej oceniane książki"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "Losowe książki"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "Lista autorów"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr "Błąd otwierania e-booka. Plik nie istnieje lub plik nie jest dostępny:"
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "Lista serii"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "Seria: %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "Dostępne języki"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "Język: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "Lista kategorii"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "Kategoria: %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "Statystyki"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "Serwer uruchomiony ponownie, proszę odświeżyć stronę"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "Wykonano wyłączenie serwera, proszę zamknąć okno"
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "Aktualizacja zakończona"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "szukaj"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "Przeczytane książki"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "Nieprzeczytane książki"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "Czytaj książkę"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "Proszę wypełnić wszystkie pola!"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "rejestracja"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "Wystąpił nieznany błąd. Spróbuj ponownie później."
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "Nazwa użytkownika lub adres e-mail jest już w użyciu."
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "Zalogowałeś się jako: '%(nickname)s'"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "Błędna nazwa użytkownika lub hasło"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "logowanie"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "Proszę najpierw skonfigurować ustawienia SMTP poczty e-mail..."
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "Książka została pomyślnie wysłana do %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "Wystąpił błąd podczas wysyłania tej książki: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "Proszę najpierw skonfigurować adres e-mail swojego kindla..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "Książka została dodana do półki: %(sname)s"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "Książka została usunięta z półki: %(sname)s"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "Półka o nazwie '%(title)s' już istnieje."
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "Półka %(title)s została utworzona"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "Wystąpił błąd"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "utwórz półkę"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "Półka %(title)s została zmieniona"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "Edytuj półkę"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "pomyślnie usunięto półkę %(name)s"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "Półka: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "Zmieniono kolejność półki: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "Znaleziono istniejące konto dla tego adresu e-mail."
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "Profil użytkownika %(name)s"
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "Zaktualizowano profil"
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "Portal administracyjny"
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Konfiguracja Calibre-web została zaktualizowana"
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "Podstawowa konfiguracja"
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "Lokalizacja bazy danych jest nieprawidłowa, wpisz poprawną ścieżkę"
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "Dodaj nowego użytkownika"
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Użytkownik '%(user)s' został utworzony"
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr "Znaleziono istniejące konto dla tego adresu e-mail lub nazwy użytkownika."
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "Zaktualizowano ustawienia poczty e-mail"
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "Testowy e-mail został pomyślnie wysłany do %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "Wystąpił błąd podczas wysyłania testowej wiadomości e-mail: %(res)s"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "Zaktualizowano ustawienia e-mail"
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "Edytuj ustawienia poczty e-mail"
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Użytkownik '%(nick)s' został usunięty"
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Użytkownik '%(nick)s' został zaktualizowany"
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "Wystąpił nieznany błąd."
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "Edytuj użytkownika %(nick)s"
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "edytuj metadane"
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "Rozszerzenie pliku \"%s\" nie jest dozwolone do przesłania na ten serwer"
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "Plik do przesłania musi mieć rozszerzenie"
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "Nie udało się utworzyć łącza %s (Odmowa dostępu)."
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "Nie można przechowywać pliku %s (Odmowa dostępu)."
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "Nie udało się usunąć pliku %s (Odmowa dostępu)."
|
||||
@ -607,6 +607,18 @@ msgstr "Na pewno chcesz zatrzymać Calibre Web?"
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "Aktualizowanie, proszę nie odświeżać strony"
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr ""
|
||||
@ -615,12 +627,13 @@ msgstr ""
|
||||
msgid "Book Title"
|
||||
msgstr "Tytuł książki"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "Autor"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "Opis"
|
||||
|
||||
@ -703,35 +716,35 @@ msgstr " Szukaj słowa kluczowego "
|
||||
msgid "Go!"
|
||||
msgstr "Idź!"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "Kliknij okładkę, aby załadować metadane do formularza"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "Ładowanie..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "Zamknij"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "Błąd wyszukiwania!"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Brak wyników! Spróbuj innego słowa kluczowego."
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "Wydawca"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "Źródło"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "Błąd wyszukiwania!"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Brak wyników! Spróbuj innego słowa kluczowego."
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Lokalizacja bazy danych Calibre"
|
||||
@ -822,7 +835,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "Domyślne ustawienia dla nowych użytkowników"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "Użytkownik z uprawnieniami administratora"
|
||||
|
||||
@ -925,6 +938,11 @@ msgstr "Zapisz ustawienia i wyślij testową wiadomość e-mail"
|
||||
msgid "Next"
|
||||
msgstr "Następne"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Szukaj"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "Odkrywaj (losowe książki)"
|
||||
@ -933,52 +951,47 @@ msgstr "Odkrywaj (losowe książki)"
|
||||
msgid "Start"
|
||||
msgstr "Rozpocznij"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Szukaj"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "Najpopularniejsze książki"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "Popularne publikacje z tego katalogu bazujące na pobranych."
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "Najlepiej ocenione książki"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "Popularne publikacje z tego katalogu bazujące na ocenach."
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "Nowe książki"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "Ostatnie książki"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "Pokazuj losowe książki"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "Autorzy"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "Książki sortowane według autorów"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "Książki sortowane według kategorii"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "Książki sortowane według serii"
|
||||
|
||||
@ -1089,7 +1102,11 @@ msgstr ""
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr "Calibre Web katalog ebooków"
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
#, fuzzy
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr "Tekst pływający, gdy paski boczne są otwarte."
|
||||
@ -1139,7 +1156,7 @@ msgid "No Results for:"
|
||||
msgstr "Brak wyników dla:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr "Proszę wypróbować podobne wyszukiwanie"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1231,45 +1248,45 @@ msgid "Show all"
|
||||
msgstr "Pokaż wszystko"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "Pokaż losowe książki"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "Pokaż najpopularniejsze książki"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "Pokaż najlepiej ocenione książki"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "Pokaż wybór języka"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "Pokaż wybór serii"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "Pokaż wybór kategorii"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "Pokaż wybór autora"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "Pokaż przeczytane i nieprzeczytane"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "Pokaz losowe książki w widoku szczegółowym"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "Usuń tego użytkownika"
|
||||
|
Binary file not shown.
@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-web\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/janeczku/calibre-web\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2017-04-30 00:47+0300\n"
|
||||
"Last-Translator: Pavel Korovin <p@tristero.se>\n"
|
||||
"Language: ru\n"
|
||||
@ -26,7 +26,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "Отсутствует"
|
||||
|
||||
@ -70,373 +70,373 @@ msgstr "Отправить на Kindle"
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr "Невозоможно найти формат, подходящий для отправки по email"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "Гость"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "Проверка обновлений"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "Загрузка обновлений"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "Распаковка обновлений"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "Файлы заменены"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "Соеднинения с базой данных закрыты"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "Сервер остановлен"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "Обновления установлены, нажмите okay и перезагрузите страницу"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "Популярные книги (часто загружаемые)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "Книги с наивысшим рейтингом"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "Случайный выбор"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "Авторы"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr "Невозможно открыть книгу. Файл не существует или недоступен."
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "Серии"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "Серии: %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "Языки"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "Язык: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "Категории"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "Категория: %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "Статистика"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "Сервер перезагружен, пожалуйста, перезагрузите страницу"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "Производится остановка сервера, пожалуйста, закройте окно"
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "Обновление закончено"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "поиск"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "Прочитанные"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "Непрочитанные"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "Читать книгу"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "Пожалуйста, заполните все поля!"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "зарегистрироваться"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "Неизвестная ошибка. Пожалуйста, попробуйте позже."
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "Имя пользователя или адрес эл. почты уже используется"
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "Вы вошли как пользователь '%(nickname)s'"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "Ошибка в имени пользователя или пароле"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "войти"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "Пожалуйста, сначала сконфигурируйте параметры SMTP"
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "Книга успешно отправлена на %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "Ошибка при отправке книги: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "Пожалуйста, сначала укажите ваш kindle email..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "Книга добавлена на книжную полку: %(sname)s"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "Книга удалена с книжной полки: %(sname)s"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "Книжкная полка с названием '%(title)s' уже существует."
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "Создана книжная полка %(title)s"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "Произошла ошибка"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "создать книжную полку"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "Книжная полка %(title)s изменена"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "Изменить книжную полку"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "Книжная полка %(name)s удалена"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "Книжная полка: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "Изменить расположение книжной полки '%(name)s'"
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "Найдена учётная запись для для данного адреса email."
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "Профиль %(name)s"
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "Профиль обновлён"
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "Администрирование"
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Конфигурация Calibre-web обновлена"
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "Настройки сервера"
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "Неверный путь к фалу БД, пожалуйста, укажите правильное расположение БД"
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "Добавить пользователя"
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "Пользователь '%(user)s' добавлен"
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr "Для указанного адреса или имени найдена существующая учётная запись."
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "Настройки почты изменены"
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "Тестовое сообщение успешно отправлено на адрес %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "Ошибка отправки тестового сообщения: %(res)s"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "Обновлены настройки e-mail"
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "Изменить почтовые настройки"
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "Пользователь '%(nick)s' удалён"
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "Пользователь '%(nick)s' обновлён"
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "Произошла неизвестная ошибка."
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "Изменить пользователя %(nick)s"
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "изменить метаданные"
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr "неизвестно"
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "Запрещена загрузка файлов с расширением \"%s\""
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "Загружаемый файл должен иметь расширение"
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "Ошибка при создании пути %s (доступ запрещён)"
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "Ошибка записи файоа %s (доступ запрещён)"
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "Ошибка удаления файла %s (доступ запрещён)"
|
||||
@ -607,6 +607,18 @@ msgstr "Вы действительно хотите остановить Calibr
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "Установка обновлений, пожалуйста, не обновляйте страницу."
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr "Удалить книгу"
|
||||
@ -615,12 +627,13 @@ msgstr "Удалить книгу"
|
||||
msgid "Book Title"
|
||||
msgstr "Название"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "Автор"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "Описание"
|
||||
|
||||
@ -703,35 +716,35 @@ msgstr " Поиск по ключевому слову"
|
||||
msgid "Go!"
|
||||
msgstr "Искать"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "Нажмите на обложку, чтобы получить метаданные"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "Загрузка..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "Закрыть"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "Ошибка поиска!"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Нет результатов. Пожалуйста, попробуйте другое ключевое слово"
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "Издатель"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "Источник"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "Ошибка поиска!"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "Нет результатов. Пожалуйста, попробуйте другое ключевое слово"
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Расположение БД Calibre"
|
||||
@ -821,7 +834,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "Настройки по умолчанию для новых пользователей"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "Управление сервером"
|
||||
|
||||
@ -922,6 +935,11 @@ msgstr "Сохранить настройки и отправить тестов
|
||||
msgid "Next"
|
||||
msgstr "Дальше"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "Обзор (случайные книги)"
|
||||
@ -930,52 +948,47 @@ msgstr "Обзор (случайные книги)"
|
||||
msgid "Start"
|
||||
msgstr "Старт"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "Поиск"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "Популярные книги"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "Популярные книги в этом каталоге, на основе количества скачиваний"
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "Книги с наилучшим рейтингом"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "Популярные книги из этого каталога на основании рейтинга"
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "Новые"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "Последние поступления"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "Показывать случайные книги"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "Авторы"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "Книги, отсортированные по автору"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "Книги, отсортированные по категории"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "Книги, отсортированные по серии"
|
||||
|
||||
@ -1086,7 +1099,11 @@ msgstr ""
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr "Каталог электронных книг Calibre Web"
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr "Обновить размещение текста при открытии боковой панели"
|
||||
|
||||
@ -1135,8 +1152,8 @@ msgid "No Results for:"
|
||||
msgstr "Ничего не найдено по запросу:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgstr "Попробуйте изменить критерии поиска"
|
||||
msgid "Please try a different search"
|
||||
msgstr "Попробуйте изменить критерии поиск"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
msgid "Results for:"
|
||||
@ -1227,45 +1244,45 @@ msgid "Show all"
|
||||
msgstr "Всех"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "Показывать случайные книги"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "Показывать популярные книги"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "Показывать книги с наивысшим рейтингом"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "Показывать выбор языка"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "Показывать выбор серии"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "Показывать выбор категории"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "Показывать выбор автора"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "Показывать прочитанные и непрочитанные"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "Показывать случайные книги при просмотре деталей"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "Удалить этого пользователя"
|
||||
|
Binary file not shown.
@ -15,7 +15,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Calibre-web\n"
|
||||
"Report-Msgid-Bugs-To: https://github.com/janeczku/calibre-web\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: 2017-01-06 17:00+0000\n"
|
||||
"Last-Translator: dalin <dalin.lin@gmail.com>\n"
|
||||
"Language: zh_Hans_CN\n"
|
||||
@ -26,7 +26,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr "未安装"
|
||||
|
||||
@ -70,373 +70,373 @@ msgstr "发送到Kindle"
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr "无法找到适合邮件发送的格式"
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr "游客"
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr "正在请求更新包"
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr "正在下载更新包"
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr "正在解压更新包"
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr "文件已替换"
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr "数据库连接已关闭"
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr "服务器已停止"
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr "更新完成,请按确定并刷新页面"
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr "最近添加的书籍"
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr "最新书籍"
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr "最旧书籍"
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr "书籍 (A-Z)"
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr "书籍 (Z-A)"
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "热门书籍(最多下载)"
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr "最高评分书籍"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr "随机书籍"
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr "作者列表"
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr "无法打开电子书。 文件不存在或者文件不可访问:"
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr "丛书列表"
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "丛书: %(serie)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr "可用语言"
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "语言: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr "分类列表"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "分类: %(name)s"
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr "可执行权限缺失"
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr "统计"
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr "服务器已重启,请刷新页面"
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr "正在关闭服务器,请关闭窗口"
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr "更新完成"
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr "已读书籍"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr "未读书籍"
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr "阅读一本书"
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "请填写所有字段"
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr "注册"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "发生一个未知错误。请稍后再试。"
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "此用户名或邮箱已被使用。"
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "您现在已以'%(nickname)s'身份登录"
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "用户名或密码错误"
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr "登录"
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr "找不到Token"
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr "Token已过期"
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr "成功!请返回您的设备"
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "请先配置SMTP邮箱..."
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "此书已被成功发给 %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "发送这本书的时候出现错误: %(res)s"
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "请先配置您的kindle电子邮箱地址..."
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "此书已被添加到书架: %(sname)s"
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "此书已从书架 %(sname)s 中删除"
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "已存在书架 '%(title)s'。"
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "书架 %(title)s 已被创建"
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr "发生错误"
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr "创建书架"
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "书架 %(title)s 已被修改"
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr "编辑书架"
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "成功删除书架 %(name)s"
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "书架: '%(name)s'"
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr "打开书架出错。书架不存在或不可访问"
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "修改书架 '%(name)s' 顺序"
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "找到已使用此邮箱的账号。"
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "%(name)s 的资料"
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr "资料已更新"
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr "管理页"
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr "Calibre-web配置已更新"
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr "基本配置"
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr "DB位置无效,请输入正确路径"
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr "添加新用户"
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "用户 '%(user)s' 已被创建"
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr "已存在使用此邮箱或昵称的账号。"
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr "邮箱设置已更新"
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "测试邮件已成功发送到 %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "发送测试邮件时发生错误: %(res)s"
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr "E-Mail 设置已更新"
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr "编辑邮箱设置"
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "用户 '%(nick)s' 已被删除"
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "用户 '%(nick)s' 已被更新"
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr "发生未知错误。"
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "编辑用户 %(nick)s"
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr "打开电子书出错。文件不存在或不可访问"
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr "编辑元数据"
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr "不能上传后缀为 \"%s\" 的文件到此服务器"
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr "要上传的文件必须有一个后缀"
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "创建路径 %s 失败(权限拒绝)。"
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "存储文件 %s 失败(权限拒绝)。"
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "删除文件 %s 失败(权限拒绝)。"
|
||||
@ -607,6 +607,18 @@ msgstr "您确定要关闭 Calibre-web 吗?"
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr "正在更新,请不要刷新页面"
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr "删除书籍"
|
||||
@ -615,12 +627,13 @@ msgstr "删除书籍"
|
||||
msgid "Book Title"
|
||||
msgstr "书名"
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "作者"
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr "简介"
|
||||
|
||||
@ -703,35 +716,35 @@ msgstr "搜索关键字"
|
||||
msgid "Go!"
|
||||
msgstr "走起!"
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr "点击封面加载元数据到表单"
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr "加载中..."
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr "关闭"
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr "搜索错误"
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "没有结果!请尝试别的关键字."
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr "出版社"
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr "来源"
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr "搜索错误"
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr "没有结果!请尝试别的关键字."
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr "Calibre 数据库位置"
|
||||
@ -821,7 +834,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr "新用户默认设置"
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr "管理用户"
|
||||
|
||||
@ -922,6 +935,11 @@ msgstr "保存设置并发送测试邮件"
|
||||
msgid "Next"
|
||||
msgstr "下一个"
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "发现(随机书籍)"
|
||||
@ -930,52 +948,47 @@ msgstr "发现(随机书籍)"
|
||||
msgid "Start"
|
||||
msgstr "开始"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr "热门书籍"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr "基于下载数的热门书籍"
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr "最高评分书籍"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "基于评分的热门书籍"
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr "新书"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr "最新书籍"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr "显示随机书籍"
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr "作者"
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "书籍按作者排序"
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr "书籍按分类排序"
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr "书籍按丛书排序"
|
||||
|
||||
@ -1086,7 +1099,11 @@ msgstr "通过魔法链接登录"
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr ""
|
||||
|
||||
@ -1135,7 +1152,7 @@ msgid "No Results for:"
|
||||
msgstr "找不到结果:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr "请尝试别的关键字"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1227,45 +1244,45 @@ msgid "Show all"
|
||||
msgstr "显示全部"
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr "显示随机书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr "显示热门书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr "显示最高评分书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr "显示语言选择"
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr "显示丛书选择"
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr "显示分类选择"
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr "显示作者选择"
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr "显示已读和未读"
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr "在详情页显示随机书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr "删除此用户"
|
||||
|
31
cps/ub.py
31
cps/ub.py
@ -6,6 +6,7 @@ from sqlalchemy import exc
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import *
|
||||
from flask_login import AnonymousUserMixin
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from werkzeug.security import generate_password_hash
|
||||
@ -50,7 +51,7 @@ DEVELOPMENT = False
|
||||
|
||||
|
||||
class UserBase:
|
||||
@staticmethod
|
||||
@classmethod
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
@ -171,6 +172,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
|
||||
settings = session.query(Settings).first()
|
||||
self.nickname = data.nickname
|
||||
self.role = data.role
|
||||
self.id=data.id
|
||||
self.sidebar_view = data.sidebar_view
|
||||
self.default_language = data.default_language
|
||||
self.locale = data.locale
|
||||
@ -186,6 +188,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
|
||||
def is_anonymous(self):
|
||||
return self.anon_browse
|
||||
|
||||
def is_authenticated(self):
|
||||
return False
|
||||
|
||||
# Baseclass representing Shelfs in calibre-web inapp.db
|
||||
class Shelf(Base):
|
||||
@ -212,15 +216,26 @@ class BookShelf(Base):
|
||||
def __repr__(self):
|
||||
return '<Book %r>' % self.id
|
||||
|
||||
|
||||
class ReadBook(Base):
|
||||
__tablename__ = 'book_read_link'
|
||||
|
||||
id=Column(Integer, primary_key=True)
|
||||
id = Column(Integer, primary_key=True)
|
||||
book_id = Column(Integer, unique=False)
|
||||
user_id =Column(Integer, ForeignKey('user.id'), unique=False)
|
||||
user_id = Column(Integer, ForeignKey('user.id'), unique=False)
|
||||
is_read = Column(Boolean, unique=False)
|
||||
|
||||
|
||||
class Bookmark(Base):
|
||||
__tablename__ = 'bookmark'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
user_id = Column(Integer, ForeignKey('user.id'))
|
||||
book_id = Column(Integer)
|
||||
format = Column(String(collation='NOCASE'))
|
||||
bookmark_key = Column(String)
|
||||
|
||||
|
||||
# Baseclass representing Downloads from calibre-web in app.db
|
||||
class Downloads(Base):
|
||||
__tablename__ = 'downloads'
|
||||
@ -376,7 +391,11 @@ class Config:
|
||||
(self.config_default_role & ROLE_DELETE_BOOKS == ROLE_DELETE_BOOKS))
|
||||
|
||||
def mature_content_tags(self):
|
||||
return list(map(unicode.lstrip, self.config_mature_content_tags.split(",")))
|
||||
if (sys.version_info > (3, 0)): #Python3 str, Python2 unicode
|
||||
lstrip = str.lstrip
|
||||
else:
|
||||
lstrip = unicode.lstrip
|
||||
return list(map(lstrip, self.config_mature_content_tags.split(",")))
|
||||
|
||||
def get_Log_Level(self):
|
||||
ret_value=""
|
||||
@ -396,7 +415,9 @@ class Config:
|
||||
# rows with SQL commands
|
||||
def migrate_Database():
|
||||
if not engine.dialect.has_table(engine.connect(), "book_read_link"):
|
||||
ReadBook.__table__.create(bind = engine)
|
||||
ReadBook.__table__.create(bind=engine)
|
||||
if not engine.dialect.has_table(engine.connect(), "bookmark"):
|
||||
Bookmark.__table__.create(bind=engine)
|
||||
|
||||
try:
|
||||
session.query(exists().where(User.locale)).scalar()
|
||||
|
279
cps/web.py
279
cps/web.py
@ -13,6 +13,12 @@ try:
|
||||
except ImportError:
|
||||
goodreads_support = False
|
||||
|
||||
try:
|
||||
import Levenshtein
|
||||
levenshtein_support = True
|
||||
except ImportError:
|
||||
levenshtein_support = False
|
||||
|
||||
try:
|
||||
from functools import reduce
|
||||
except ImportError:
|
||||
@ -73,6 +79,7 @@ import hashlib
|
||||
from redirect import redirect_back, is_safe_url
|
||||
|
||||
from tornado import version as tornadoVersion
|
||||
from socket import error as SocketError
|
||||
|
||||
try:
|
||||
from urllib.parse import quote
|
||||
@ -151,7 +158,7 @@ class Gauth:
|
||||
@Singleton
|
||||
class Gdrive:
|
||||
def __init__(self):
|
||||
self.drive = gdriveutils.getDrive(Gauth.Instance().auth)
|
||||
self.drive = gdriveutils.getDrive(gauth=Gauth.Instance().auth)
|
||||
|
||||
|
||||
class ReverseProxied(object):
|
||||
@ -196,6 +203,7 @@ class ReverseProxied(object):
|
||||
mimetypes.init()
|
||||
mimetypes.add_type('application/xhtml+xml', '.xhtml')
|
||||
mimetypes.add_type('application/epub+zip', '.epub')
|
||||
mimetypes.add_type('application/fb2+zip', '.fb2')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.mobi')
|
||||
mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
|
||||
mimetypes.add_type('application/vnd.amazon.ebook', '.azw')
|
||||
@ -511,7 +519,7 @@ def common_filters():
|
||||
if current_user.filter_language() != "all":
|
||||
lang_filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
|
||||
else:
|
||||
lang_filter = True
|
||||
lang_filter = true()
|
||||
content_rating_filter = false() if current_user.mature_content else \
|
||||
db.Books.tags.any(db.Tags.name.in_(config.mature_content_tags()))
|
||||
return and_(lang_filter, ~content_rating_filter)
|
||||
@ -528,7 +536,7 @@ def fill_indexpage(page, database, db_filter, order):
|
||||
pagination = Pagination(page, config.config_books_per_page,
|
||||
len(db.session.query(database)
|
||||
.filter(db_filter).filter(common_filters()).all()))
|
||||
entries = db.session.query(database).filter(common_filters())\
|
||||
entries = db.session.query(database).filter(db_filter).filter(common_filters())\
|
||||
.order_by(order).offset(off).limit(config.config_books_per_page)
|
||||
return entries, random, pagination
|
||||
|
||||
@ -624,7 +632,7 @@ def before_request():
|
||||
def feed_index():
|
||||
xml = render_title_template('index.xml')
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -633,7 +641,7 @@ def feed_index():
|
||||
def feed_osd():
|
||||
xml = render_title_template('osd.xml', lang='de-DE')
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -663,7 +671,7 @@ def feed_search(term):
|
||||
else:
|
||||
xml = render_title_template('feed.xml', searchterm="")
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -677,7 +685,7 @@ def feed_new():
|
||||
db.Books, True, db.Books.timestamp.desc())
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -689,7 +697,7 @@ def feed_discover():
|
||||
pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page))
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -703,7 +711,7 @@ def feed_best_rated():
|
||||
db.Books, db.Books.ratings.any(db.Ratings.rating > 9), db.Books.timestamp.desc())
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -731,7 +739,7 @@ def feed_hot():
|
||||
pagination = Pagination((int(off) / (int(config.config_books_per_page)) + 1), config.config_books_per_page, numBooks)
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -747,7 +755,7 @@ def feed_authorindex():
|
||||
len(db.session.query(db.Authors).all()))
|
||||
xml = render_title_template('feed.xml', listelements=entries, folder='feed_author', pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -761,7 +769,7 @@ def feed_author(book_id):
|
||||
db.Books, db.Books.authors.any(db.Authors.id == book_id), db.Books.timestamp.desc())
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -777,7 +785,7 @@ def feed_categoryindex():
|
||||
len(db.session.query(db.Tags).all()))
|
||||
xml = render_title_template('feed.xml', listelements=entries, folder='feed_category', pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -787,11 +795,11 @@ def feed_category(book_id):
|
||||
off = request.args.get("offset")
|
||||
if not off:
|
||||
off = 0
|
||||
entries, random, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||
entries, __, pagination = fill_indexpage((int(off) / (int(config.config_books_per_page)) + 1),
|
||||
db.Books, db.Books.tags.any(db.Tags.id == book_id), db.Books.timestamp.desc())
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -807,7 +815,7 @@ def feed_seriesindex():
|
||||
len(db.session.query(db.Series).all()))
|
||||
xml = render_title_template('feed.xml', listelements=entries, folder='feed_series', pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -821,7 +829,7 @@ def feed_series(book_id):
|
||||
db.Books, db.Books.series.any(db.Series.id == book_id),db.Books.series_index)
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
response.headers["Content-Type"] = "application/xml"
|
||||
response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
|
||||
return response
|
||||
|
||||
|
||||
@ -858,7 +866,7 @@ def get_opds_download_link(book_id, book_format):
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()).first()
|
||||
app.logger.info(data.name)
|
||||
if current_user.is_authenticated:
|
||||
if current_user.is_authenticated():
|
||||
helper.update_download(book_id, int(current_user.id))
|
||||
file_name = book.title
|
||||
if len(book.authors) > 0:
|
||||
@ -866,7 +874,11 @@ def get_opds_download_link(book_id, book_format):
|
||||
file_name = helper.get_valid_filename(file_name)
|
||||
headers = Headers()
|
||||
headers["Content-Disposition"] = "attachment; filename*=UTF-8''%s.%s" % (quote(file_name.encode('utf8')), book_format)
|
||||
app.logger.info(time.time()-startTime)
|
||||
try:
|
||||
headers["Content-Type"] = mimetypes.types_map['.' + book_format]
|
||||
except KeyError:
|
||||
headers["Content-Type"] = "application/octet-stream"
|
||||
app.logger.info(time.time() - startTime)
|
||||
startTime = time.time()
|
||||
if config.config_use_google_drive:
|
||||
app.logger.info(time.time() - startTime)
|
||||
@ -1078,12 +1090,9 @@ def hot_books(page):
|
||||
hot_books = all_books.offset(off).limit(config.config_books_per_page)
|
||||
entries = list()
|
||||
for book in hot_books:
|
||||
downloadBook = db.session.query(db.Books).filter(db.Books.id == book.Downloads.book_id).first()
|
||||
downloadBook = db.session.query(db.Books).filter(common_filters()).filter(db.Books.id == book.Downloads.book_id).first()
|
||||
if downloadBook:
|
||||
entries.append(
|
||||
db.session.query(db.Books).filter(common_filters())
|
||||
.filter(db.Books.id == book.Downloads.book_id).first()
|
||||
)
|
||||
entries.append(downloadBook)
|
||||
else:
|
||||
ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete()
|
||||
ub.session.commit()
|
||||
@ -1118,6 +1127,8 @@ def author_list():
|
||||
entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count'))\
|
||||
.join(db.books_authors_link).join(db.Books).filter(common_filters())\
|
||||
.group_by('books_authors_link.author').order_by(db.Authors.sort).all()
|
||||
for entry in entries:
|
||||
entry.Authors.name=entry.Authors.name.replace('|',',')
|
||||
return render_title_template('list.html', entries=entries, folder='author', title=_(u"Author list"))
|
||||
|
||||
|
||||
@ -1125,30 +1136,45 @@ def author_list():
|
||||
@app.route("/author/<int:book_id>/<int:page>'")
|
||||
@login_required_if_no_ano
|
||||
def author(book_id, page):
|
||||
entries, random, pagination = fill_indexpage(page, db.Books, db.Books.authors.any(db.Authors.id == book_id),
|
||||
entries, __, pagination = fill_indexpage(page, db.Books, db.Books.authors.any(db.Authors.id == book_id),
|
||||
db.Books.timestamp.desc())
|
||||
if entries is None:
|
||||
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
name = db.session.query(db.Authors).filter(db.Authors.id == book_id).first().name
|
||||
name = (db.session.query(db.Authors).filter(db.Authors.id == book_id).first().name).replace('|',',')
|
||||
|
||||
author_info = None
|
||||
other_books = None
|
||||
other_books = []
|
||||
if goodreads_support and config.config_use_goodreads:
|
||||
gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret)
|
||||
author_info = gc.find_author(author_name=name)
|
||||
|
||||
# Get all identifiers (ISBN, Goodreads, etc) and filter author's books by that list so we show fewer duplicates
|
||||
# Note: Not all images will be shown, even though they're available on Goodreads.com.
|
||||
# See https://www.goodreads.com/topic/show/18213769-goodreads-book-images
|
||||
identifiers = reduce(lambda acc, book: acc + map(lambda identifier: identifier.val, book.identifiers), entries.all(), [])
|
||||
other_books = filter(lambda book: book.isbn not in identifiers and book.gid["#text"] not in identifiers, author_info.books)
|
||||
other_books = get_unique_other_books(entries.all(), author_info.books)
|
||||
|
||||
return render_title_template('author.html', entries=entries, pagination=pagination,
|
||||
title=name, author=author_info, other_books=other_books)
|
||||
|
||||
|
||||
def get_unique_other_books(library_books, author_books):
|
||||
# Get all identifiers (ISBN, Goodreads, etc) and filter author's books by that list so we show fewer duplicates
|
||||
# Note: Not all images will be shown, even though they're available on Goodreads.com.
|
||||
# See https://www.goodreads.com/topic/show/18213769-goodreads-book-images
|
||||
identifiers = reduce(lambda acc, book: acc + map(lambda identifier: identifier.val, book.identifiers), library_books, [])
|
||||
other_books = filter(lambda book: book.isbn not in identifiers and book.gid["#text"] not in identifiers, author_books)
|
||||
|
||||
# Fuzzy match book titles
|
||||
if levenshtein_support:
|
||||
library_titles = reduce(lambda acc, book: acc + [book.title], library_books, [])
|
||||
other_books = filter(lambda author_book: not filter(
|
||||
lambda library_book:
|
||||
Levenshtein.ratio(re.sub(r"\(.*\)", "", author_book.title), library_book) > 0.7, # Remove items in parentheses before comparing
|
||||
library_titles
|
||||
), other_books)
|
||||
|
||||
return other_books
|
||||
|
||||
|
||||
|
||||
@app.route("/series")
|
||||
@login_required_if_no_ano
|
||||
def series_list():
|
||||
@ -1186,13 +1212,12 @@ def language_overview():
|
||||
lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
|
||||
else:
|
||||
try:
|
||||
langfound = 1
|
||||
cur_l = LC.parse(current_user.filter_language())
|
||||
except Exception:
|
||||
langfound = 0
|
||||
cur_l = None
|
||||
languages = db.session.query(db.Languages).filter(
|
||||
db.Languages.lang_code == current_user.filter_language()).all()
|
||||
if langfound:
|
||||
if cur_l:
|
||||
languages[0].name = cur_l.get_language_name(get_locale())
|
||||
else:
|
||||
languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name)
|
||||
@ -1298,6 +1323,26 @@ def show_book(book_id):
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@app.route("/ajax/bookmark/<int:book_id>/<book_format>", methods=['POST'])
|
||||
@login_required
|
||||
def bookmark(book_id, book_format):
|
||||
bookmark_key = request.form["bookmark"]
|
||||
ub.session.query(ub.Bookmark).filter(ub.and_(ub.Bookmark.user_id == int(current_user.id),
|
||||
ub.Bookmark.book_id == book_id,
|
||||
ub.Bookmark.format == book_format)).delete()
|
||||
if not bookmark_key:
|
||||
ub.session.commit()
|
||||
return "", 204
|
||||
|
||||
bookmark = ub.Bookmark(user_id=current_user.id,
|
||||
book_id=book_id,
|
||||
format=book_format,
|
||||
bookmark_key=bookmark_key)
|
||||
ub.session.merge(bookmark)
|
||||
ub.session.commit()
|
||||
return "", 201
|
||||
|
||||
|
||||
@app.route("/admin")
|
||||
@login_required
|
||||
def admin_forbidden():
|
||||
@ -1672,15 +1717,21 @@ def feed_get_cover(book_id):
|
||||
|
||||
|
||||
def render_read_books(page, are_read, as_xml=False):
|
||||
readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id)).filter(ub.ReadBook.is_read == True).all()
|
||||
readBookIds = [x.book_id for x in readBooks]
|
||||
if are_read:
|
||||
db_filter = db.Books.id.in_(readBookIds)
|
||||
else:
|
||||
db_filter = ~db.Books.id.in_(readBookIds)
|
||||
if not current_user.is_anonymous():
|
||||
readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id)).filter(ub.ReadBook.is_read == True).all()
|
||||
readBookIds = [x.book_id for x in readBooks]
|
||||
if are_read:
|
||||
db_filter = db.Books.id.in_(readBookIds)
|
||||
else:
|
||||
db_filter = ~db.Books.id.in_(readBookIds)
|
||||
|
||||
entries, random, pagination = fill_indexpage(page, db.Books,
|
||||
db_filter, db.Books.timestamp.desc())
|
||||
else:
|
||||
entries = []
|
||||
random = False
|
||||
pagination = Pagination(page, 1, 0)
|
||||
|
||||
entries, random, pagination = fill_indexpage(page, db.Books,
|
||||
db_filter, db.Books.timestamp.desc())
|
||||
if as_xml:
|
||||
xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
|
||||
response = make_response(xml)
|
||||
@ -1732,49 +1783,55 @@ def unread_books(page):
|
||||
@login_required_if_no_ano
|
||||
def read_book(book_id, book_format):
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
if book:
|
||||
book_dir = os.path.join(config.get_main_dir, "cps", "static", str(book_id))
|
||||
if not os.path.exists(book_dir):
|
||||
os.mkdir(book_dir)
|
||||
if book_format.lower() == "epub":
|
||||
# check if mimetype file is exists
|
||||
mime_file = str(book_id) + "/mimetype"
|
||||
if not os.path.exists(mime_file):
|
||||
epub_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + ".epub"
|
||||
if not os.path.isfile(epub_file):
|
||||
raise ValueError('Error opening eBook. File does not exist: ', epub_file)
|
||||
zfile = zipfile.ZipFile(epub_file)
|
||||
for name in zfile.namelist():
|
||||
(dirName, fileName) = os.path.split(name)
|
||||
newDir = os.path.join(book_dir, dirName)
|
||||
if not os.path.exists(newDir):
|
||||
try:
|
||||
os.makedirs(newDir)
|
||||
except OSError as exception:
|
||||
if not exception.errno == errno.EEXIST:
|
||||
raise
|
||||
if fileName:
|
||||
fd = open(os.path.join(newDir, fileName), "wb")
|
||||
fd.write(zfile.read(name))
|
||||
fd.close()
|
||||
zfile.close()
|
||||
return render_title_template('read.html', bookid=book_id, title=_(u"Read a Book"))
|
||||
elif book_format.lower() == "pdf":
|
||||
return render_title_template('readpdf.html', pdffile=book_id, title=_(u"Read a Book"))
|
||||
elif book_format.lower() == "txt":
|
||||
return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book"))
|
||||
elif book_format.lower() == "cbr":
|
||||
all_name = str(book_id) + "/" + book.data[0].name + ".cbr"
|
||||
tmp_file = os.path.join(book_dir, book.data[0].name) + ".cbr"
|
||||
if not os.path.exists(all_name):
|
||||
cbr_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + ".cbr"
|
||||
copyfile(cbr_file, tmp_file)
|
||||
return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"))
|
||||
|
||||
else:
|
||||
if not book:
|
||||
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
book_dir = os.path.join(config.get_main_dir, "cps", "static", str(book_id))
|
||||
if not os.path.exists(book_dir):
|
||||
os.mkdir(book_dir)
|
||||
bookmark = None
|
||||
if current_user.is_authenticated():
|
||||
bookmark = ub.session.query(ub.Bookmark).filter(ub.and_(ub.Bookmark.user_id == int(current_user.id),
|
||||
ub.Bookmark.book_id == book_id,
|
||||
ub.Bookmark.format == book_format.upper())).first()
|
||||
if book_format.lower() == "epub":
|
||||
# check if mimetype file is exists
|
||||
mime_file = str(book_id) + "/mimetype"
|
||||
if not os.path.exists(mime_file):
|
||||
epub_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + ".epub"
|
||||
if not os.path.isfile(epub_file):
|
||||
raise ValueError('Error opening eBook. File does not exist: ', epub_file)
|
||||
zfile = zipfile.ZipFile(epub_file)
|
||||
for name in zfile.namelist():
|
||||
(dirName, fileName) = os.path.split(name)
|
||||
newDir = os.path.join(book_dir, dirName)
|
||||
if not os.path.exists(newDir):
|
||||
try:
|
||||
os.makedirs(newDir)
|
||||
except OSError as exception:
|
||||
if not exception.errno == errno.EEXIST:
|
||||
raise
|
||||
if fileName:
|
||||
fd = open(os.path.join(newDir, fileName), "wb")
|
||||
fd.write(zfile.read(name))
|
||||
fd.close()
|
||||
zfile.close()
|
||||
return render_title_template('read.html', bookid=book_id, title=_(u"Read a Book"), bookmark=bookmark)
|
||||
elif book_format.lower() == "pdf":
|
||||
return render_title_template('readpdf.html', pdffile=book_id, title=_(u"Read a Book"))
|
||||
elif book_format.lower() == "txt":
|
||||
return render_title_template('readtxt.html', txtfile=book_id, title=_(u"Read a Book"))
|
||||
else:
|
||||
for fileext in ["cbr","cbt","cbz"]:
|
||||
if book_format.lower() == fileext:
|
||||
all_name = str(book_id) + "/" + book.data[0].name + "." + fileext
|
||||
tmp_file = os.path.join(book_dir, book.data[0].name) + "." + fileext
|
||||
if not os.path.exists(all_name):
|
||||
cbr_file = os.path.join(config.config_calibre_dir, book.path, book.data[0].name) + "." + fileext
|
||||
copyfile(cbr_file, tmp_file)
|
||||
return render_title_template('readcbr.html', comicfile=all_name, title=_(u"Read a Book"))
|
||||
|
||||
|
||||
@app.route("/download/<int:book_id>/<book_format>")
|
||||
@login_required_if_no_ano
|
||||
@ -1785,7 +1842,7 @@ def get_download_link(book_id, book_format):
|
||||
data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()).first()
|
||||
if data:
|
||||
# collect downloaded books only for registered user and not for anonymous user
|
||||
if current_user.is_authenticated:
|
||||
if current_user.is_authenticated():
|
||||
helper.update_download(book_id, int(current_user.id))
|
||||
file_name = book.title
|
||||
if len(book.authors) > 0:
|
||||
@ -1819,7 +1876,7 @@ def get_download_link_ext(book_id, book_format, anyname):
|
||||
def register():
|
||||
if not config.config_public_reg:
|
||||
abort(404)
|
||||
if current_user is not None and current_user.is_authenticated:
|
||||
if current_user is not None and current_user.is_authenticated():
|
||||
return redirect(url_for('index'))
|
||||
|
||||
if request.method == "POST":
|
||||
@ -1856,7 +1913,7 @@ def register():
|
||||
def login():
|
||||
if not config.db_configured:
|
||||
return redirect(url_for('basic_configuration'))
|
||||
if current_user is not None and current_user.is_authenticated:
|
||||
if current_user is not None and current_user.is_authenticated():
|
||||
return redirect(url_for('index'))
|
||||
if request.method == "POST":
|
||||
form = request.form.to_dict()
|
||||
@ -1883,7 +1940,7 @@ def login():
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
if current_user is not None and current_user.is_authenticated:
|
||||
if current_user is not None and current_user.is_authenticated():
|
||||
logout_user()
|
||||
return redirect(url_for('login'))
|
||||
|
||||
@ -2128,10 +2185,11 @@ def edit_shelf(shelf_id):
|
||||
@login_required
|
||||
def delete_shelf(shelf_id):
|
||||
cur_shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
|
||||
deleted = false
|
||||
if current_user.role_admin():
|
||||
deleted = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).delete()
|
||||
else:
|
||||
if not cur_shelf.is_public and not cur_shelf.user_id == int(current_user.id) \
|
||||
if (not cur_shelf.is_public and cur_shelf.user_id == int(current_user.id)) \
|
||||
or (cur_shelf.is_public and current_user.role_edit_shelfs()):
|
||||
deleted = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id),
|
||||
ub.Shelf.id == shelf_id),
|
||||
@ -2697,7 +2755,7 @@ def edit_book(book_id):
|
||||
except Exception:
|
||||
book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name)
|
||||
for author in book.authors:
|
||||
author_names.append(author.name)
|
||||
author_names.append(author.name.replace('|',','))
|
||||
|
||||
# Show form
|
||||
if request.method != 'POST':
|
||||
@ -2706,12 +2764,41 @@ def edit_book(book_id):
|
||||
|
||||
# Update book
|
||||
edited_books_id = set()
|
||||
|
||||
# Check and handle Uploaded file
|
||||
if 'btn-upload-format' in request.files and '.' in request.files['btn-upload-format'].filename:
|
||||
requested_file = request.files['btn-upload-format']
|
||||
file_ext = requested_file.filename.rsplit('.', 1)[-1].lower()
|
||||
if file_ext not in ALLOWED_EXTENSIONS:
|
||||
flash(_('File extension "%s" is not allowed to be uploaded to this server' % file_ext), category="error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
file_name = book.path.rsplit('/', 1)[-1]
|
||||
filepath = config.config_calibre_dir + os.sep + book.path
|
||||
filepath = os.path.normpath(filepath)
|
||||
saved_filename = filepath + os.sep + file_name + '.' + file_ext
|
||||
|
||||
try:
|
||||
requested_file.save(saved_filename)
|
||||
except OSError:
|
||||
flash(_(u"Failed to store file %s." % saved_filename), category="error")
|
||||
return redirect(url_for('index'))
|
||||
|
||||
file_size = os.path.getsize(saved_filename)
|
||||
is_format = db.session.query(db.Data).filter(db.Data.book == book_id).filter(db.Data.format == file_ext.upper()).first()
|
||||
if is_format:
|
||||
# Format entry already exists, no need to update the database
|
||||
pass
|
||||
else:
|
||||
db_format = db.Data(book_id, file_ext.upper(), file_size, file_name)
|
||||
db.session.add(db_format)
|
||||
|
||||
to_save = request.form.to_dict()
|
||||
if book.title != to_save["book_title"]:
|
||||
book.title = to_save["book_title"]
|
||||
edited_books_id.add(book.id)
|
||||
input_authors = to_save["author_name"].split('&')
|
||||
input_authors = map(lambda it: it.strip(), input_authors)
|
||||
input_authors = map(lambda it: it.strip().replace(',','|'), input_authors)
|
||||
# we have all author names now
|
||||
if input_authors == ['']:
|
||||
input_authors = [_(u'unknown')] # prevent empty Author
|
||||
@ -3028,5 +3115,11 @@ def upload():
|
||||
def start_gevent():
|
||||
from gevent.wsgi import WSGIServer
|
||||
global gevent_server
|
||||
gevent_server = WSGIServer(('', ub.config.config_port), app)
|
||||
gevent_server.serve_forever()
|
||||
try:
|
||||
gevent_server = WSGIServer(('', ub.config.config_port), app)
|
||||
gevent_server.serve_forever()
|
||||
except SocketError:
|
||||
app.logger.info('Unable to listen on \'\', trying on IPv4 only...')
|
||||
gevent_server = WSGIServer(('0.0.0.0', ub.config.config_port), app)
|
||||
gevent_server.serve_forever()
|
||||
|
||||
|
293
messages.pot
293
messages.pot
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-08-12 18:55+0200\n"
|
||||
"POT-Creation-Date: 2017-09-16 07:48+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.4.0\n"
|
||||
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1374
|
||||
#: cps/book_formats.py:118 cps/book_formats.py:122 cps/web.py:1358
|
||||
msgid "not installed"
|
||||
msgstr ""
|
||||
|
||||
@ -61,373 +61,373 @@ msgstr ""
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr ""
|
||||
|
||||
#: cps/ub.py:542
|
||||
#: cps/ub.py:556
|
||||
msgid "Guest"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:974
|
||||
#: cps/web.py:953
|
||||
msgid "Requesting update package"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:975
|
||||
#: cps/web.py:954
|
||||
msgid "Downloading update package"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:976
|
||||
#: cps/web.py:955
|
||||
msgid "Unzipping update package"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:977
|
||||
#: cps/web.py:956
|
||||
msgid "Files are replaced"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:978
|
||||
#: cps/web.py:957
|
||||
msgid "Database connections are closed"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:979
|
||||
#: cps/web.py:958
|
||||
msgid "Server is stopped"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:980
|
||||
#: cps/web.py:959
|
||||
msgid "Update finished, please press okay and reload page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1054
|
||||
#: cps/web.py:1033
|
||||
msgid "Recently Added Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1063
|
||||
#: cps/web.py:1042
|
||||
msgid "Newest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1072
|
||||
#: cps/web.py:1051
|
||||
msgid "Oldest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1081
|
||||
#: cps/web.py:1060
|
||||
msgid "Books (A-Z)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1090
|
||||
#: cps/web.py:1069
|
||||
msgid "Books (Z-A)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1126
|
||||
#: cps/web.py:1096
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1136
|
||||
#: cps/web.py:1106
|
||||
msgid "Best rated books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:36 cps/web.py:1145
|
||||
#: cps/templates/index.xml:35 cps/web.py:1115
|
||||
msgid "Random Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1161
|
||||
#: cps/web.py:1124
|
||||
msgid "Author list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1181 cps/web.py:1212 cps/web.py:1351 cps/web.py:1835
|
||||
#: cps/web.py:1134 cps/web.py:1190 cps/web.py:1315 cps/web.py:1774
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:71 cps/web.py:1198
|
||||
#: cps/templates/index.xml:70 cps/web.py:1176
|
||||
msgid "Series list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1210
|
||||
#: cps/web.py:1188
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1221
|
||||
msgid "Available languages"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1258
|
||||
#: cps/web.py:1236
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:64 cps/web.py:1274
|
||||
#: cps/templates/index.xml:63 cps/web.py:1245
|
||||
msgid "Category list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1257
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1385
|
||||
#: cps/web.py:1369
|
||||
msgid "Excecution permissions missing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1399
|
||||
#: cps/web.py:1383
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1563
|
||||
#: cps/web.py:1547
|
||||
msgid "Server restarted, please reload page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1565
|
||||
#: cps/web.py:1549
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1581
|
||||
#: cps/web.py:1565
|
||||
msgid "Update done"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1662 cps/web.py:1675
|
||||
#: cps/web.py:1640 cps/web.py:1653
|
||||
msgid "search"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:43 cps/templates/index.xml:47
|
||||
#: cps/templates/layout.html:127 cps/web.py:1751
|
||||
#: cps/templates/index.xml:42 cps/templates/index.xml:46
|
||||
#: cps/templates/layout.html:127 cps/web.py:1729
|
||||
msgid "Read Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:50 cps/templates/index.xml:54
|
||||
#: cps/templates/layout.html:128 cps/web.py:1754
|
||||
#: cps/templates/index.xml:49 cps/templates/index.xml:53
|
||||
#: cps/templates/layout.html:128 cps/web.py:1732
|
||||
msgid "Unread Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1821 cps/web.py:1823 cps/web.py:1825 cps/web.py:1832
|
||||
#: cps/web.py:1805 cps/web.py:1807 cps/web.py:1809 cps/web.py:1816
|
||||
msgid "Read a Book"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1888 cps/web.py:2513
|
||||
#: cps/web.py:1868 cps/web.py:2493
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1889 cps/web.py:1905 cps/web.py:1910 cps/web.py:1912
|
||||
#: cps/web.py:1869 cps/web.py:1885 cps/web.py:1890 cps/web.py:1892
|
||||
msgid "register"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:1884
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:1889
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1928 cps/web.py:2024
|
||||
#: cps/web.py:1908 cps/web.py:2004
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1933
|
||||
#: cps/web.py:1913
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1939 cps/web.py:1960
|
||||
#: cps/web.py:1919 cps/web.py:1940
|
||||
msgid "login"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1972 cps/web.py:2003
|
||||
#: cps/web.py:1952 cps/web.py:1983
|
||||
msgid "Token not found"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1980 cps/web.py:2011
|
||||
#: cps/web.py:1960 cps/web.py:1991
|
||||
msgid "Token has expired"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1988
|
||||
#: cps/web.py:1968
|
||||
msgid "Success! Please return to your device"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2038
|
||||
#: cps/web.py:2018
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2042
|
||||
#: cps/web.py:2022
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2046
|
||||
#: cps/web.py:2026
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2048 cps/web.py:2598
|
||||
#: cps/web.py:2028 cps/web.py:2578
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2092
|
||||
#: cps/web.py:2072
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2127
|
||||
#: cps/web.py:2107
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2146 cps/web.py:2170
|
||||
#: cps/web.py:2126 cps/web.py:2150
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2151
|
||||
#: cps/web.py:2131
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2153 cps/web.py:2181
|
||||
#: cps/web.py:2133 cps/web.py:2161
|
||||
msgid "There was an error"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2154 cps/web.py:2156
|
||||
#: cps/web.py:2134 cps/web.py:2136
|
||||
msgid "create a shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2179
|
||||
#: cps/web.py:2159
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2182 cps/web.py:2184
|
||||
#: cps/web.py:2162 cps/web.py:2164
|
||||
msgid "Edit a shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2204
|
||||
#: cps/web.py:2184
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2226
|
||||
#: cps/web.py:2206
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2229
|
||||
#: cps/web.py:2209
|
||||
msgid "Error opening shelf. Shelf does not exist or is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2261
|
||||
#: cps/web.py:2241
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2326
|
||||
#: cps/web.py:2306
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2328 cps/web.py:2332
|
||||
#: cps/web.py:2308 cps/web.py:2312
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2329
|
||||
#: cps/web.py:2309
|
||||
msgid "Profile updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2343
|
||||
#: cps/web.py:2323
|
||||
msgid "Admin page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2467
|
||||
#: cps/web.py:2447
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2474 cps/web.py:2480 cps/web.py:2494
|
||||
#: cps/web.py:2454 cps/web.py:2460 cps/web.py:2474
|
||||
msgid "Basic Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2478
|
||||
#: cps/web.py:2458
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:34 cps/web.py:2515 cps/web.py:2568
|
||||
#: cps/templates/admin.html:34 cps/web.py:2495 cps/web.py:2548
|
||||
msgid "Add new user"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2560
|
||||
#: cps/web.py:2540
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2564
|
||||
#: cps/web.py:2544
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2586
|
||||
#: cps/web.py:2566
|
||||
msgid "Mail settings updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2593
|
||||
#: cps/web.py:2573
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2596
|
||||
#: cps/web.py:2576
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2600
|
||||
#: cps/web.py:2580
|
||||
msgid "E-Mail settings updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2601
|
||||
#: cps/web.py:2581
|
||||
msgid "Edit mail settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2630
|
||||
#: cps/web.py:2610
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2728
|
||||
#: cps/web.py:2708
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2731
|
||||
#: cps/web.py:2711
|
||||
msgid "An unknown error occured."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2734
|
||||
#: cps/web.py:2714
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2756
|
||||
#: cps/web.py:2730
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2771 cps/web.py:2954 cps/web.py:3078
|
||||
#: cps/web.py:2745 cps/web.py:2917 cps/web.py:3060
|
||||
msgid "edit metadata"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2783 cps/web.py:2787
|
||||
#: cps/web.py:2757 cps/web.py:2761
|
||||
msgid "unknown"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2972
|
||||
#: cps/web.py:2954
|
||||
#, python-format
|
||||
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2978
|
||||
#: cps/web.py:2960
|
||||
msgid "File to be uploaded must have an extension"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2997
|
||||
#: cps/web.py:2979
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:3002
|
||||
#: cps/web.py:2984
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:3007
|
||||
#: cps/web.py:2989
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr ""
|
||||
@ -598,6 +598,18 @@ msgstr ""
|
||||
msgid "Updating, please do not reload page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:15
|
||||
msgid "via"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:23
|
||||
msgid "In Library"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/author.html:69
|
||||
msgid "More by"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:16
|
||||
msgid "Delete Book"
|
||||
msgstr ""
|
||||
@ -606,12 +618,13 @@ msgstr ""
|
||||
msgid "Book Title"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188
|
||||
#: cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
|
||||
#: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:190
|
||||
#: cps/templates/book_edit.html:30 cps/templates/book_edit.html:213
|
||||
#: cps/templates/book_edit.html:228
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
@ -694,35 +707,35 @@ msgstr ""
|
||||
msgid "Go!"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:168
|
||||
#: cps/templates/book_edit.html:171
|
||||
msgid "Click the cover to load metadata to the form"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:172 cps/templates/book_edit.html:185
|
||||
#: cps/templates/book_edit.html:183 cps/templates/book_edit.html:223
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:175 cps/templates/layout.html:199
|
||||
#: cps/templates/book_edit.html:188 cps/templates/layout.html:199
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:186
|
||||
msgid "Search error!"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:187
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125
|
||||
#: cps/templates/search_form.html:14
|
||||
#: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
|
||||
#: cps/templates/detail.html:125 cps/templates/search_form.html:14
|
||||
msgid "Publisher"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:191
|
||||
#: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
|
||||
msgid "Source"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:224
|
||||
msgid "Search error!"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/book_edit.html:225
|
||||
msgid "No Result! Please try anonther keyword."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr ""
|
||||
@ -812,7 +825,7 @@ msgstr ""
|
||||
msgid "Default Settings for new users"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:90
|
||||
#: cps/templates/config_edit.html:128 cps/templates/user_edit.html:86
|
||||
msgid "Admin user"
|
||||
msgstr ""
|
||||
|
||||
@ -913,6 +926,11 @@ msgstr ""
|
||||
msgid "Next"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/feed.xml:29 cps/templates/index.xml:7
|
||||
#: cps/templates/layout.html:40 cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr ""
|
||||
@ -921,52 +939,47 @@ msgstr ""
|
||||
msgid "Start"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:40
|
||||
#: cps/templates/layout.html:41
|
||||
msgid "Search"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:121
|
||||
#: cps/templates/index.xml:14 cps/templates/layout.html:121
|
||||
msgid "Hot Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
#: cps/templates/index.xml:18
|
||||
msgid "Popular publications from this catalog based on Downloads."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:124
|
||||
#: cps/templates/index.xml:21 cps/templates/layout.html:124
|
||||
msgid "Best rated Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
#: cps/templates/index.xml:25
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:29
|
||||
#: cps/templates/index.xml:28
|
||||
msgid "New Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
#: cps/templates/index.xml:32
|
||||
msgid "The latest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
#: cps/templates/index.xml:39
|
||||
msgid "Show Random Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:57 cps/templates/layout.html:139
|
||||
#: cps/templates/index.xml:56 cps/templates/layout.html:139
|
||||
msgid "Authors"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:61
|
||||
#: cps/templates/index.xml:60
|
||||
msgid "Books ordered by Author"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:68
|
||||
#: cps/templates/index.xml:67
|
||||
msgid "Books ordered by category"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:75
|
||||
#: cps/templates/index.xml:74
|
||||
msgid "Books ordered by series"
|
||||
msgstr ""
|
||||
|
||||
@ -1077,7 +1090,11 @@ msgstr ""
|
||||
msgid "Calibre Web ebook catalog"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:125
|
||||
#: cps/templates/read.html:69
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/read.html:72
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr ""
|
||||
|
||||
@ -1126,7 +1143,7 @@ msgid "No Results for:"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgid "Please try a different search"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
@ -1218,45 +1235,45 @@ msgid "Show all"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:46
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show random books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:54
|
||||
#: cps/templates/user_edit.html:50
|
||||
msgid "Show hot books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:58
|
||||
#: cps/templates/user_edit.html:54
|
||||
msgid "Show best rated books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:62
|
||||
#: cps/templates/user_edit.html:58
|
||||
msgid "Show language selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:66
|
||||
#: cps/templates/user_edit.html:62
|
||||
msgid "Show series selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:70
|
||||
#: cps/templates/user_edit.html:66
|
||||
msgid "Show category selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:74
|
||||
#: cps/templates/user_edit.html:70
|
||||
msgid "Show author selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:78
|
||||
#: cps/templates/user_edit.html:74
|
||||
msgid "Show read and unread"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:82
|
||||
#: cps/templates/user_edit.html:78
|
||||
msgid "Show random books in detail view"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:90
|
||||
msgid "Show mature content"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:123
|
||||
msgid "Delete this user"
|
||||
msgstr ""
|
||||
|
@ -11,4 +11,5 @@ PyYAML==3.12
|
||||
rsa==3.4.2
|
||||
six==1.10.0
|
||||
uritemplate==3.0.0
|
||||
goodreads==0.3.2
|
||||
goodreads>=0.3.2
|
||||
python-Levenshtein>=0.12.0
|
||||
|
@ -10,9 +10,9 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
||||
|
||||
- Bootstrap 3 HTML5 interface
|
||||
- full graphical setup
|
||||
- User management
|
||||
- User management with fine grained per-user permissions
|
||||
- Admin interface
|
||||
- User Interface in dutch, english, french, german, polish, russian, simplified chinese, spanish
|
||||
- User Interface in dutch, english, french, german, italian, polish, russian, simplified chinese, spanish
|
||||
- OPDS feed for eBook reader apps
|
||||
- Filter and search by titles, authors, tags, series and language
|
||||
- Create custom book collection (shelves)
|
||||
@ -21,10 +21,10 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
||||
- Restrict eBook download to logged-in users
|
||||
- Support for public user registration
|
||||
- Send eBooks to Kindle devices with the click of a button
|
||||
- Support for reading eBooks directly in the browser (.txt, .epub, .pdf)
|
||||
- Support for reading eBooks directly in the browser (.txt, .epub, .pdf, .cbr, .cbt, .cbz)
|
||||
- Upload new books in PDF, epub, fb2 format
|
||||
- Support for Calibre custom columns
|
||||
- Fine grained per-user permissions
|
||||
- Ability to hide content based on categories for certain users
|
||||
- Self update capability
|
||||
- "Magic Link" login to make it easy to log on eReaders
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user