+2
-2
@@ -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
|
||||
|
||||
+33
-70
@@ -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:
|
||||
|
||||
@@ -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 |
@@ -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";
|
||||
};
|
||||
|
||||
})();
|
||||
+42
-33
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -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
@@ -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 );
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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 %}
|
||||
|
||||
+11
-18
@@ -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>
|
||||
|
||||
+24
-59
@@ -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>
|
||||
|
||||
@@ -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
Binary file not shown.
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 "删除此用户"
|
||||
|
||||
@@ -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()
|
||||
|
||||
+186
-93
@@ -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()
|
||||
|
||||
|
||||
+155
-138
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user