Merge pull request #14 from janeczku/master

merge from janeczku/master
pull/610/head
Ethan Lin 7 years ago committed by GitHub
commit 54d8240dc0

@ -20,11 +20,11 @@ def init_cache_busting(app):
app.logger.debug('Computing cache-busting values...') app.logger.debug('Computing cache-busting values...')
# compute file hashes # compute file hashes
for dirpath, dirnames, filenames in os.walk(static_folder): for dirpath, __, filenames in os.walk(static_folder):
for filename in filenames: for filename in filenames:
# compute version component # compute version component
rooted_filename = os.path.join(dirpath, filename) 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] file_hash = hashlib.md5(f.read()).hexdigest()[:7]
# save version to tables # save version to tables

@ -73,26 +73,27 @@ if not os.path.exists(dbpath):
migrate() migrate()
def getDrive(gauth=None): def getDrive(drive=None, 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 getEbooksFolder(drive=None):
if not drive: if not drive:
drive = getDrive() 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: if drive.auth.access_token_expired:
drive.auth.Refresh() drive.auth.Refresh()
return drive
def getEbooksFolder(drive=None):
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 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() fileList = drive.ListFile({'q': ebooksFolder}).GetList()
@ -113,20 +114,14 @@ def getEbooksFolderId(drive=None):
def getFolderInFolder(parentId, folderName, drive=None): def getFolderInFolder(parentId, folderName, drive=None):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
folder = "title = '%s' and '%s' in parents and mimeType = 'application/vnd.google-apps.folder' and trashed = false" % (folderName.replace("'", "\\'"), parentId) 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() fileList = drive.ListFile({'q': folder}).GetList()
return fileList[0] return fileList[0]
def getFile(pathId, fileName, drive=None): def getFile(pathId, fileName, drive=None):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'")) metaDataFile = "'%s' in parents and trashed = false and title = '%s'" % (pathId, fileName.replace("'", "\\'"))
fileList = drive.ListFile({'q': metaDataFile}).GetList() fileList = drive.ListFile({'q': metaDataFile}).GetList()
@ -134,10 +129,7 @@ def getFile(pathId, fileName, drive=None):
def getFolderId(path, drive=None): def getFolderId(path, drive=None):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
currentFolderId = getEbooksFolderId(drive) currentFolderId = getEbooksFolderId(drive)
sqlCheckPath = path if path[-1] == '/' else path + '/' sqlCheckPath = path if path[-1] == '/' else path + '/'
storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first() storedPathName = session.query(GdriveId).filter(GdriveId.path == sqlCheckPath).first()
@ -168,10 +160,7 @@ def getFolderId(path, drive=None):
def getFileFromEbooksFolder(drive, path, fileName): def getFileFromEbooksFolder(drive, path, fileName):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
if path: if path:
# sqlCheckPath=path if path[-1] =='/' else path + '/' # sqlCheckPath=path if path[-1] =='/' else path + '/'
folderId = getFolderId(path, drive) folderId = getFolderId(path, drive)
@ -182,10 +171,7 @@ def getFileFromEbooksFolder(drive, path, fileName):
def copyDriveFileRemote(drive, origin_file_id, copy_title): def copyDriveFileRemote(drive, origin_file_id, copy_title):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
copied_file = {'title': copy_title} copied_file = {'title': copy_title}
try: try:
file_data = drive.auth.service.files().copy( 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): def downloadFile(drive, path, filename, output):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
f = getFileFromEbooksFolder(drive, path, filename) f = getFileFromEbooksFolder(drive, path, filename)
f.GetContentFile(output) f.GetContentFile(output)
def backupCalibreDbAndOptionalDownload(drive, f=None): def backupCalibreDbAndOptionalDownload(drive, f=None):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
metaDataFile = "'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId() metaDataFile = "'%s' in parents and title = 'metadata.db' and trashed = false" % getEbooksFolderId()
fileList = drive.ListFile({'q': metaDataFile}).GetList() fileList = drive.ListFile({'q': metaDataFile}).GetList()
@ -221,12 +201,10 @@ def backupCalibreDbAndOptionalDownload(drive, f=None):
def copyToDrive(drive, uploadFile, createRoot, replaceFiles, def copyToDrive(drive, uploadFile, createRoot, replaceFiles,
ignoreFiles=[], ignoreFiles=None,
parent=None, prevDir=''): parent=None, prevDir=''):
if not drive: ignoreFiles = ignoreFiles or []
drive = getDrive() drive = getDrive(drive)
if drive.auth.access_token_expired:
drive.auth.Refresh()
isInitial = not bool(parent) isInitial = not bool(parent)
if not parent: if not parent:
parent = getEbooksFolder(drive) parent = getEbooksFolder(drive)
@ -254,10 +232,7 @@ def copyToDrive(drive, uploadFile, createRoot, replaceFiles,
def uploadFileToEbooksFolder(drive, destFile, f): def uploadFileToEbooksFolder(drive, destFile, f):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
parent = getEbooksFolder(drive) parent = getEbooksFolder(drive)
splitDir = destFile.split('/') splitDir = destFile.split('/')
for i, x in enumerate(splitDir): for i, x in enumerate(splitDir):
@ -281,10 +256,7 @@ def uploadFileToEbooksFolder(drive, destFile, f):
def watchChange(drive, channel_id, channel_type, channel_address, def watchChange(drive, channel_id, channel_type, channel_address,
channel_token=None, expiration=None): channel_token=None, expiration=None):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
# Watch for all changes to a user's Drive. # Watch for all changes to a user's Drive.
# Args: # Args:
# service: Drive API service instance. # service: Drive API service instance.
@ -327,10 +299,7 @@ def watchFile(drive, file_id, channel_id, channel_type, channel_address,
Raises: Raises:
apiclient.errors.HttpError: if http request to create channel fails. apiclient.errors.HttpError: if http request to create channel fails.
""" """
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
body = { body = {
'id': channel_id, 'id': channel_id,
@ -353,10 +322,7 @@ def stopChannel(drive, channel_id, resource_id):
Raises: Raises:
apiclient.errors.HttpError: if http request to create channel fails. apiclient.errors.HttpError: if http request to create channel fails.
""" """
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
# service=drive.auth.service # service=drive.auth.service
body = { body = {
'id': channel_id, 'id': channel_id,
@ -366,10 +332,7 @@ def stopChannel(drive, channel_id, resource_id):
def getChangeById (drive, change_id): def getChangeById (drive, change_id):
if not drive: drive = getDrive(drive)
drive = getDrive()
if drive.auth.access_token_expired:
drive.auth.Refresh()
# Print a single Change resource information. # Print a single Change resource information.
# #
# Args: # Args:

@ -266,6 +266,7 @@ def get_valid_filename(value, replace_whitespace=True):
""" """
if value[-1:] == u'.': if value[-1:] == u'.':
value = value[:-1]+u'_' value = value[:-1]+u'_'
value = value.replace("/", "_").replace(":", "_").strip('\0')
if use_unidecode: if use_unidecode:
value=(unidecode.unidecode(value)).strip() value=(unidecode.unidecode(value)).strip()
else: 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; } #meta-info img { max-height: 150px; max-width: 100px; cursor: pointer; }
.padded-bottom { margin-bottom: 15px; } .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";
};
})();

@ -4,31 +4,31 @@
/* global Bloodhound, language, Modernizr, tinymce */ /* global Bloodhound, language, Modernizr, tinymce */
if ($("#description").length) { if ($("#description").length) {
tinymce.init({ tinymce.init({
selector: "#description", selector: "#description",
branding: false, branding: false,
menubar: "edit view format", menubar: "edit view format",
language language: language
}); });
if (!Modernizr.inputtypes.date) { if (!Modernizr.inputtypes.date) {
$("#pubdate").datepicker({ $("#pubdate").datepicker({
format: "yyyy-mm-dd", format: "yyyy-mm-dd",
language language: language
}).on("change", function () { }).on("change", function () {
// Show localized date over top of the standard YYYY-MM-DD date // Show localized date over top of the standard YYYY-MM-DD date
let pubDate; var pubDate;
const results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD var results = /(\d{4})[-\/\\](\d{1,2})[-\/\\](\d{1,2})/.exec(this.value); // YYYY-MM-DD
if (results) { if (results) {
pubDate = new Date(results[1], parseInt(results[2], 10)-1, results[3]) || new Date(this.value); pubDate = new Date(results[1], parseInt(results[2], 10) - 1, results[3]) || new Date(this.value);
} $("#fake_pubdate")
$("#fake_pubdate") .val(pubDate.toLocaleDateString(language))
.val(pubDate.toLocaleDateString(language)) .removeClass("hidden");
.removeClass("hidden"); }
}).trigger("change"); }).trigger("change");
} }
} }
/* /*
Takes a prefix, query typeahead callback, Bloodhound typeahead adapter Takes a prefix, query typeahead callback, Bloodhound typeahead adapter
and returns the completions it gets from the bloodhound engine prefixed. and returns the completions it gets from the bloodhound engine prefixed.
@ -43,6 +43,7 @@ function prefixedSource(prefix, query, cb, bhAdapter) {
cb(matches); cb(matches);
}); });
} }
function getPath() { function getPath() {
var jsFileLocation = $("script[src*=edit_books]").attr("src"); // the js file path var jsFileLocation = $("script[src*=edit_books]").attr("src"); // the js file path
jsFileLocation = jsFileLocation.replace("/static/js/edit_books.js", ""); // the js folder 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, queryTokenizer: Bloodhound.tokenizers.whitespace,
remote: { 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]; return [query];
}, },
remote: { remote: {
url: getPath()+"/get_series_json?q=", url: getPath() + "/get_series_json?q=",
replace: function replace(url, query) { 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) { queryTokenizer: function queryTokenizer(query) {
var tokens = query.split(","); var tokens = query.split(",");
tokens = [tokens[tokens.length-1].trim()]; tokens = [tokens[tokens.length - 1].trim()];
return tokens; return tokens;
}, },
remote: { 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]; return [query];
}, },
remote: { remote: {
url: getPath()+"/get_languages_json?q=", url: getPath() + "/get_languages_json?q=",
replace: function replace(url, query) { 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 bhAdapter = source.ttAdapter();
var tokens = query.split(split); 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 prefix = "";
var newSplit; var newSplit;
if (split === "&") { if (split === "&") {
@ -192,7 +193,7 @@ promiseLanguages.done(function() {
$("#search").on("change input.typeahead:selected", function() { $("#search").on("change input.typeahead:selected", function() {
var form = $("form").serialize(); var form = $("form").serialize();
$.getJSON( getPath()+"/get_matching_tags", form, function( data ) { $.getJSON( getPath() + "/get_matching_tags", form, function( data ) {
$(".tags_click").each(function() { $(".tags_click").each(function() {
if ($.inArray(parseInt($(this).children("input").first().val(), 10), data.tags) === -1 ) { if ($.inArray(parseInt($(this).children("input").first().val(), 10), data.tags) === -1 ) {
if (!($(this).hasClass("active"))) { 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> * Created by idalin<dalin.lin@gmail.com>
* Google Books api document: https://developers.google.com/books/docs/v1/using * 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) * Douban Books api document: https://developers.douban.com/wiki/?title=book_v2 (Chinese Only)
*/ */
/* global _, i18nMsg, tinymce */ /* global _, i18nMsg, tinymce */
var dbResults = []; var dbResults = [];
var ggResults = []; var ggResults = [];
@ -103,6 +103,10 @@ $(function () {
} }
}; };
if (book.rating > 0) {
book.rating /= 2;
}
var $book = $(templates.bookResult(book)); var $book = $(templates.bookResult(book));
$book.find("img").on("click", function () { $book.find("img").on("click", function () {
populateForm(book); 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();
}
});
}
}

@ -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({ $(".load-more .row").infinitescroll({
debug: false, debug: false,
navSelector : ".pagination", navSelector : ".pagination",
// selector for the paged navigation (it will be hidden) // selector for the paged navigation (it will be hidden)
nextSelector : ".pagination a:last", nextSelector : ".pagination a:last",
// selector for the NEXT link (to page 2) // selector for the NEXT link (to page 2)
itemSelector : ".load-more .book", itemSelector : ".load-more .book",
animate : true, animate : true,
extraScrollPx: 300 extraScrollPx: 300
// selector for all items you'll retrieve // selector for all items you'll retrieve
}, function(data) { }, function(data) {
$(".load-more .row").isotope( "appended", $(data), null ); $(".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="title">{{entry.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.authors %} {% 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 %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}
@ -64,9 +64,9 @@
</div> </div>
</div> </div>
{% if other_books is not none %} {% if other_books %}
<div class="discover"> <div class="discover">
<h3>{{_("More by")}} {{ author.name|safe }}</h3> <h3>{{_("More by")}} {{ author.name.replace('|',',')|safe }}</h3>
<div class="row"> <div class="row">
{% for entry in other_books %} {% for entry in other_books %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div class="col-sm-3 col-lg-2 col-xs-6 book">
@ -80,7 +80,7 @@
<p class="author"> <p class="author">
{% for author in entry.authors %} {% for author in entry.authors %}
<a href="https://www.goodreads.com/author/show/{{ author.gid }}" target="_blank" rel="noopener"> <a href="https://www.goodreads.com/author/show/{{ author.gid }}" target="_blank" rel="noopener">
{{author.name}} {{author.name.replace('|',',')}}
</a> </a>
{% if not loop.last %} {% if not loop.last %}
&amp; &amp;

@ -1,7 +1,7 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
{% if book %} {% 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="col-sm-3 col-lg-3 col-xs-12">
<div class="cover"> <div class="cover">
@ -115,7 +115,15 @@
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% 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"> <div class="checkbox">
<label> <label>

@ -22,7 +22,7 @@
</button> </button>
{% for format in entry.data %} {% 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"> <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> </a>
{% endfor %} {% endfor %}
{% else %} {% else %}
@ -32,7 +32,7 @@
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1"> <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
{% for format in entry.data %} {% 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 %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
@ -62,7 +62,7 @@
<h2>{{entry.title}}</h2> <h2>{{entry.title}}</h2>
<p class="author"> <p class="author">
{% for author in entry.authors %} {% 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 %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}

@ -33,7 +33,7 @@
</div> </div>
<button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save settings')}}</button> <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> <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> </form>
</div> </div>

@ -64,8 +64,7 @@
<entry> <entry>
<title>{{entry.name}}</title> <title>{{entry.name}}</title>
<id>{{ url_for(folder, book_id=entry.id) }}</id> <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 rel="subsection" type="application/atom+xml;profile=opds-catalog" href="{{url_for(folder, book_id=entry.id)}}"/>
<link type="application/atom+xml" href="{{url_for(folder, book_id=entry.id)}}" rel="subsection"/>
</entry> </entry>
{% endfor %} {% endfor %}
</feed> </feed>

@ -6,7 +6,7 @@
<div class="row"> <div class="row">
{% for entry in random %} {% 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"> <div class="cover">
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %} {% if entry.has_cover %}
@ -42,7 +42,7 @@
<div class="row"> <div class="row">
{% if entries[0] %} {% if entries[0] %}
{% for entry in entries %} {% 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"> <div class="cover">
<a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false"> <a href="{{ url_for('show_book', book_id=entry.id) }}" data-toggle="modal" data-target="#bookDetailsModal" data-remote="false">
{% if entry.has_cover %} {% if entry.has_cover %}
@ -56,7 +56,7 @@
<p class="title">{{entry.title|shortentitle}}</p> <p class="title">{{entry.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.authors %} {% 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 %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}

@ -12,64 +12,57 @@
</author> </author>
<entry> <entry>
<title>{{_('Hot Books')}}</title> <title>{{_('Hot Books')}}</title>
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_hot')}}" /> <link rel="http://opds-spec.org/sort/popular" href="{{url_for('feed_hot')}}" type="application/atom+xml;profile=opds-catalog"/>
<link type="application/atom+xml" href="{{url_for('feed_hot')}}" rel="http://opds-spec.org/sort/popular"/>
<id>{{url_for('feed_hot')}}</id> <id>{{url_for('feed_hot')}}</id>
<content type="text">{{_('Popular publications from this catalog based on Downloads.')}}</content> <content type="text">{{_('Popular publications from this catalog based on Downloads.')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Best rated Books')}}</title> <title>{{_('Best rated Books')}}</title>
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_best_rated')}}" /> <link rel="http://opds-spec.org/recommended" href="{{url_for('feed_best_rated')}}" type="application/atom+xml;profile=opds-catalog"/>
<link type="application/atom+xml" href="{{url_for('feed_best_rated')}}" rel="http://opds-spec.org/recommended"/>
<id>{{url_for('feed_best_rated')}}</id> <id>{{url_for('feed_best_rated')}}</id>
<content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content> <content type="text">{{_('Popular publications from this catalog based on Rating.')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('New Books')}}</title> <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"/>
<link rel="http://opds-spec.org/sort/new" href="{{url_for('feed_new')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
<id>{{url_for('feed_new')}}</id> <id>{{url_for('feed_new')}}</id>
<content type="text">{{_('The latest Books')}}</content> <content type="text">{{_('The latest Books')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Random Books')}}</title> <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"/>
<link rel="http://opds-spec.org/featured" href="{{url_for('feed_discover')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
<id>{{url_for('feed_discover')}}</id> <id>{{url_for('feed_discover')}}</id>
<content type="text">{{_('Show Random Books')}}</content> <content type="text">{{_('Show Random Books')}}</content>
</entry> </entry>
{% if not current_user.is_anonymous() %}
<entry> <entry>
<title>{{_('Read Books')}}</title> <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"/>
<link rel="subsection" href="{{url_for('feed_read_books')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
<id>{{url_for('feed_read_books')}}</id> <id>{{url_for('feed_read_books')}}</id>
<content type="text">{{_('Read Books')}}</content> <content type="text">{{_('Read Books')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Unread Books')}}</title> <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"/>
<link rel="subsection" href="{{url_for('feed_unread_books')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"/>
<id>{{url_for('feed_unread_books')}}</id> <id>{{url_for('feed_unread_books')}}</id>
<content type="text">{{_('Unread Books')}}</content> <content type="text">{{_('Unread Books')}}</content>
</entry> </entry>
{% endif %}
<entry> <entry>
<title>{{_('Authors')}}</title> <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"/>
<link rel="subsection" href="{{url_for('feed_authorindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
<id>{{url_for('feed_authorindex')}}</id> <id>{{url_for('feed_authorindex')}}</id>
<content type="text">{{_('Books ordered by Author')}}</content> <content type="text">{{_('Books ordered by Author')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Category list')}}</title> <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"/>
<link rel="subsection" href="{{url_for('feed_categoryindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
<id>{{url_for('feed_categoryindex')}}</id> <id>{{url_for('feed_categoryindex')}}</id>
<content type="text">{{_('Books ordered by category')}}</content> <content type="text">{{_('Books ordered by category')}}</content>
</entry> </entry>
<entry> <entry>
<title>{{_('Series list')}}</title> <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"/>
<link rel="subsection" href="{{url_for('feed_seriesindex')}}" type="application/atom+xml;profile=opds-catalog;type=feed;kind=navigation"/>
<id>{{url_for('feed_seriesindex')}}</id> <id>{{url_for('feed_seriesindex')}}</id>
<content type="text">{{_('Books ordered by series')}}</content> <content type="text">{{_('Books ordered by series')}}</content>
</entry> </entry>

@ -41,7 +41,7 @@
"rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %}, "rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
"authors": [ "authors": [
{% for author in entry.authors %} {% for author in entry.authors %}
"{{author.name}}"{% if not loop.last %},{% endif %} "{{author.name.replace('|',',')}}"{% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
], ],
"other_formats": { "other_formats": {

@ -32,9 +32,9 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="{{url_for('index')}}">Calibre Web</a> <a class="navbar-brand" href="{{url_for('index')}}">{{instance}}</a>
</div> </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"> <form class="navbar-form navbar-left" role="search" action="{{url_for('search')}}" method="GET">
<div class="form-group input-group input-group-sm"> <div class="form-group input-group input-group-sm">
<label for="query" class="sr-only">{{_('Search')}}</label> <label for="query" class="sr-only">{{_('Search')}}</label>
@ -46,13 +46,13 @@
</form> </form>
{% endif %} {% endif %}
<div class="navbar-collapse collapse"> <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 "> <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> <li><a href="{{url_for('advanced_search')}}"><span class="glyphicon glyphicon-search"></span><span class="hidden-sm"> {{_('Advanced Search')}}</span></a></li>
</ul> </ul>
{% endif %} {% endif %}
<ul class="nav navbar-nav navbar-right" id="main-nav"> <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.user.role_upload() or g.user.role_admin()%}
{% if g.allow_upload %} {% if g.allow_upload %}
<li> <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> <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 %}
{% 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> <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> <li><a id="register" href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
{% endif %} {% endif %}
</ul> </ul>
@ -99,7 +101,7 @@
{% endfor %} {% endfor %}
<div class="container-fluid"> <div class="container-fluid">
<div class="row-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"> <div class="col-sm-2">
<nav class="navigation"> <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"> <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() %} {% 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> <li id="nav_lang"><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li>
{%endif%} {%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> <li class="nav-head hidden-xs">{{_('Public Shelves')}}</li>
{% for shelf in g.public_shelfes %} {% 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> <li><a href="{{url_for('show_shelf', shelf_id=shelf.id)}}"><span class="glyphicon glyphicon-list"></span> {{shelf.name}}</a></li>

@ -11,59 +11,6 @@
<link rel="stylesheet" href="{{ url_for('static', filename='css/libs/normalize.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/libs/normalize.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/popup.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> </head>
<body> <body>
<div id="sidebar"> <div id="sidebar">
@ -73,24 +20,24 @@
<!--a id="show-Search" class="show_view icon-search" data-view="Search">Search</a--> <!--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-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-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>
<div id="tocView" class="view"> <div id="tocView" class="view">
</div> </div>
<div id="searchView" class="view"> <!--div id="searchView" class="view">
<ul id="searchResults"></ul> <ul id="searchResults"></ul>
</div> </div-->
<div id="bookmarksView" class="view"> <div id="bookmarksView" class="view">
<ul id="bookmarks"></ul> <ul id="bookmarks"></ul>
</div> </div>
<div id="notesView" class="view"> <!--div id="notesView" class="view">
<div id="new-note"> <div id="new-note">
<textarea id="note-text"></textarea> <textarea id="note-text"></textarea>
<button id="note-anchor">Anchor</button> <button id="note-anchor">Anchor</button>
</div> </div>
<ol id="notes"></ol> <ol id="notes"></ol>
</div> </div-->
</div> </div>
<div id="main"> <div id="main">
@ -119,7 +66,7 @@
</div> </div>
<div class="modal md-effect-1" id="settings-modal"> <div class="modal md-effect-1" id="settings-modal">
<div class="md-content"> <div class="md-content">
<h3>Settings</h3> <h3>{{_('Settings')}}</h3>
<div> <div>
<p> <p>
<input type="checkbox" id="sidebarReflow" name="sidebarReflow">{{_('Reflow text when sidebars are open.')}} <input type="checkbox" id="sidebarReflow" name="sidebarReflow">{{_('Reflow text when sidebars are open.')}}
@ -129,5 +76,23 @@
</div> </div>
</div> </div>
<div class="overlay"></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> </body>
</html> </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 %} {% if entries|length < 1 %}
<h2>{{_('No Results for:')}} {{searchterm}}</h2> <h2>{{_('No Results for:')}} {{searchterm}}</h2>
<p>{{_('Please try a diffrent Search')}}</p> <p>{{_('Please try a different search')}}</p>
{% else %} {% else %}
<h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2> <h2>{{entries|length}} {{_('Results for:')}} {{searchterm}}</h2>
{%endif%} {%endif%}
@ -24,7 +24,7 @@
<p class="title">{{entry.title|shortentitle}}</p> <p class="title">{{entry.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.authors %} {% 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 %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
<h2>{{title}}</h2> <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 %} {% 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> <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> <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="title">{{entry.title|shortentitle}}</p>
<p class="author"> <p class="author">
{% for author in entry.authors %} {% 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 %} {% if not loop.last %}
&amp; &amp;
{% endif %} {% endif %}

@ -1,5 +1,9 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% 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> <h3>{{_('Calibre library statistics')}}</h3>
<table id="stats" class="table"> <table id="stats" class="table">
<tbody> <tbody>

@ -41,10 +41,6 @@
</select> </select>
</div> </div>
<div class="col-sm-6"> <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"> <div class="form-group">
<input type="checkbox" name="show_random" id="show_random" {% if content.show_random_books() %}checked{% endif %}> <input type="checkbox" name="show_random" id="show_random" {% if content.show_random_books() %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label> <label for="show_random">{{_('Show random books')}}</label>
@ -89,6 +85,10 @@
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}> <input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
<label for="admin_role">{{_('Admin user')}}</label> <label for="admin_role">{{_('Admin user')}}</label>
</div> </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 %} {% endif %}
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="download_role" id="download_role" {% if content.role_download() %}checked{% endif %}> <input type="checkbox" name="download_role" id="download_role" {% if content.role_download() %}checked{% endif %}>

@ -21,7 +21,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-web\n" "Project-Id-Version: Calibre-web\n"
"Report-Msgid-Bugs-To: https://github.com/janeczku/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" "PO-Revision-Date: 2016-07-12 19:54+0200\n"
"Last-Translator: Ozzie Isaacs\n" "Last-Translator: Ozzie Isaacs\n"
"Language: de\n" "Language: de\n"
@ -32,7 +32,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "Nicht installiert" msgstr "Nicht installiert"
@ -78,379 +78,379 @@ msgstr ""
"Konnte keine Formate finden welche für das versenden per E-Mail geeignet " "Konnte keine Formate finden welche für das versenden per E-Mail geeignet "
"sind" "sind"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "Gast" msgstr "Gast"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Frage Update Paket an" msgstr "Frage Update Paket an"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Lade Update Paket herunter" msgstr "Lade Update Paket herunter"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Entpacke Update Paket" msgstr "Entpacke Update Paket"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "Ersetze Dateien" msgstr "Ersetze Dateien"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Schließe Datenbankverbindungen" msgstr "Schließe Datenbankverbindungen"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "Stoppe Server" msgstr "Stoppe Server"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Update abgeschlossen, bitte okay drücken und Seite neu laden" msgstr "Update abgeschlossen, bitte okay drücken und Seite neu laden"
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "Kürzlich hinzugefügte Bücher" msgstr "Kürzlich hinzugefügte Bücher"
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "Neueste Bücher" msgstr "Neueste Bücher"
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "Älteste Bücher" msgstr "Älteste Bücher"
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "Bücher (A-Z)" msgstr "Bücher (A-Z)"
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "Bücher (Z-A)" msgstr "Bücher (Z-A)"
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Beliebte Bücher (die meisten Downloads)" msgstr "Beliebte Bücher (die meisten Downloads)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "Best bewertete Bücher" 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" msgid "Random Books"
msgstr "Zufällige Bücher" msgstr "Zufällige Bücher"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "Autorenliste" 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:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "" msgstr ""
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht " "Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
"zugänglich." "zugänglich."
#: cps/templates/index.xml:71 cps/web.py:1198 #: cps/templates/index.xml:70 cps/web.py:1176
msgid "Series list" msgid "Series list"
msgstr "Liste Serien" msgstr "Liste Serien"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Serie: %(serie)s" msgstr "Serie: %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "Verfügbare Sprachen" msgstr "Verfügbare Sprachen"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Sprache: %(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" msgid "Category list"
msgstr "Kategorieliste" msgstr "Kategorieliste"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Kategorie: %(name)s" msgstr "Kategorie: %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "Ausführungsberechtigung nicht vorhanden" msgstr "Ausführungsberechtigung nicht vorhanden"
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "Statistiken" msgstr "Statistiken"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Server neu gestartet,bitte Seite neu laden" msgstr "Server neu gestartet,bitte Seite neu laden"
#: cps/web.py:1565 #: cps/web.py:1549
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
msgstr "Server wird runtergefahren, bitte Fenster schließen" msgstr "Server wird runtergefahren, bitte Fenster schließen"
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "Update durchgeführt" msgstr "Update durchgeführt"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "Suche" msgstr "Suche"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "Gelesene Bücher" msgstr "Gelesene Bücher"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "Ungelesene Bücher" 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" msgid "Read a Book"
msgstr "Lese ein Buch" 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!" msgid "Please fill out all fields!"
msgstr "Bitte alle Felder ausfüllen!" 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" msgid "register"
msgstr "Registieren" msgstr "Registieren"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Es ist ein unbekannter Fehler aufgetreten. Bitte später erneut versuchen." 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." msgid "This username or email address is already in use."
msgstr "Der Benutzername oder die E-Mailadresse ist in bereits in Benutzung." 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 #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Du bist nun eingeloggt als '%(nickname)s'" msgstr "Du bist nun eingeloggt als '%(nickname)s'"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Falscher Benutzername oder Passwort" msgstr "Falscher Benutzername oder Passwort"
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "Login" msgstr "Login"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "Token wurde nicht gefunden" 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" msgid "Token has expired"
msgstr "Das Token ist abgelaufen" msgstr "Das Token ist abgelaufen"
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "Erfolg! Bitte zum Gerät zurückkehren" msgstr "Erfolg! Bitte zum Gerät zurückkehren"
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..." msgstr "Bitte zuerst die SMTP Mail Einstellung konfigurieren ..."
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Buch erfolgreich versandt an %(kindlemail)s" msgstr "Buch erfolgreich versandt an %(kindlemail)s"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Beim Senden des Buchs trat ein Fehler auf: %(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..." msgid "Please configure your kindle email address first..."
msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..." msgstr "Bitte die Kindle E-Mail Adresse zuuerst konfigurieren..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt" msgstr "Das Buch wurde dem Bücherregal: %(sname)s hinzugefügt"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Das Buch wurde aus dem Bücherregal: %(sname)s entfernt" 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 #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'" msgstr "Es existiert bereits ein Bücheregal mit dem Titel '%(title)s'"
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Bücherregal %(title)s erzeugt" 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" msgid "There was an error"
msgstr "Es trat ein Fehler auf" 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" msgid "create a shelf"
msgstr "Bücherregal erzeugen" msgstr "Bücherregal erzeugen"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "Bücherregal %(title)s verändert" 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" msgid "Edit a shelf"
msgstr "Bücherregal editieren" msgstr "Bücherregal editieren"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "Bücherregal %(name)s erfolgreich gelöscht" msgstr "Bücherregal %(name)s erfolgreich gelöscht"
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Bücherregal: '%(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" 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" msgstr "Fehler beim Öffnen. Bücherregel exisitert nicht oder ist nicht zugänglich"
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "Reihenfolge in Bücherregal '%(name)s' verändern" 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." msgid "Found an existing account for this email address."
msgstr "Es existiert ein Benutzerkonto für diese E-Mailadresse" 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 #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "%(name)s's Profil" msgstr "%(name)s's Profil"
#: cps/web.py:2328 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "Profil aktualisiert" msgstr "Profil aktualisiert"
#: cps/web.py:2342 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "Admin Seite" msgstr "Admin Seite"
#: cps/web.py:2466 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Calibre-web Konfiguration wurde aktualisiert" 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" msgid "Basic Configuration"
msgstr "Basis Konfiguration" msgstr "Basis Konfiguration"
#: cps/web.py:2477 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "DB Speicherort ist ungültig, bitte Pfad korrigieren" 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" msgid "Add new user"
msgstr "Neuen Benutzer hinzufügen" msgstr "Neuen Benutzer hinzufügen"
#: cps/web.py:2559 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Benutzer '%(user)s' angelegt" msgstr "Benutzer '%(user)s' angelegt"
#: cps/web.py:2563 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "" msgstr ""
"Es existiert ein Benutzerkonto für diese Emailadresse oder den " "Es existiert ein Benutzerkonto für diese Emailadresse oder den "
"Benutzernamen." "Benutzernamen."
#: cps/web.py:2585 #: cps/web.py:2566
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "E-Mail Einstellungen aktualisiert" msgstr "E-Mail Einstellungen aktualisiert"
#: cps/web.py:2592 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Test E-Mail erfolgreich an %(kindlemail)s versendet" msgstr "Test E-Mail erfolgreich an %(kindlemail)s versendet"
#: cps/web.py:2595 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "Fehler beim versenden der 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" msgid "E-Mail settings updated"
msgstr "E-Mail Einstellungen wurde aktualisiert" msgstr "E-Mail Einstellungen wurde aktualisiert"
#: cps/web.py:2600 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "E-Mail Einstellungen editieren" msgstr "E-Mail Einstellungen editieren"
#: cps/web.py:2629 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Benutzer '%(nick)s' gelöscht" msgstr "Benutzer '%(nick)s' gelöscht"
#: cps/web.py:2727 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Benutzer '%(nick)s' aktualisiert" msgstr "Benutzer '%(nick)s' aktualisiert"
#: cps/web.py:2730 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Es ist ein unbekanter Fehler aufgetreten" msgstr "Es ist ein unbekanter Fehler aufgetreten"
#: cps/web.py:2733 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Benutzer %(nick)s bearbeiten" 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" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "" msgstr ""
"Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht " "Buch öffnen fehlgeschlagen. Datei existiert nicht, oder ist nicht "
"zugänglich" "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" msgid "edit metadata"
msgstr "Metadaten editieren" msgstr "Metadaten editieren"
#: cps/web.py:2782 cps/web.py:2786 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "Unbekannt" msgstr "Unbekannt"
#: cps/web.py:2971 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" msgid "File extension \"%s\" is not allowed to be uploaded to this server"
msgstr "Die Dateiendung \"%s\" kann nicht auf diesen Server hochgeladen werden" 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" msgid "File to be uploaded must have an extension"
msgstr "Datei müssen eine Erweiterung haben, um hochgeladen zu werden" msgstr "Datei müssen eine Erweiterung haben, um hochgeladen zu werden"
#: cps/web.py:2996 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)" msgstr "Fehler beim Erzeugen des Pfads %s (Zugriff verweigert)"
#: cps/web.py:3001 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)" msgstr "Fehler beim speichern der Datei %s (Zugriff verweigert)"
#: cps/web.py:3006 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Fehler beim Löschen von Datei %s (Zugriff verweigert)" 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" msgid "Updating, please do not reload page"
msgstr "Updatevorgang, bitte Seite nicht neu laden" 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "Buch löschen" msgstr "Buch löschen"
@ -629,12 +641,13 @@ msgstr "Buch löschen"
msgid "Book Title" msgid "Book Title"
msgstr "Buchtitel" msgstr "Buchtitel"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "Autor" 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" msgid "Description"
msgstr "Beschreibung" msgstr "Beschreibung"
@ -699,7 +712,7 @@ msgstr "Das Buch wird aus der Calibre Datenbank"
#: cps/templates/book_edit.html:144 #: cps/templates/book_edit.html:144
msgid "and from hard disk" msgid "and from hard disk"
msgstr "und der Festplatte gelöscht" msgstr "und von der Festplatte gelöscht"
#: cps/templates/book_edit.html:148 #: cps/templates/book_edit.html:148
msgid "Delete" msgid "Delete"
@ -717,35 +730,35 @@ msgstr "Suchbegriff"
msgid "Go!" msgid "Go!"
msgstr "Los!" msgstr "Los!"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "Klicke auf das Bild um die Metadaten zu übertragen" 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..." msgid "Loading..."
msgstr "Lade..." 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" msgid "Close"
msgstr "Schließen" msgstr "Schließen"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "Herausgeber" msgstr "Herausgeber"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "Quelle" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Speicherort der Calibre Datenbank" msgstr "Speicherort der Calibre Datenbank"
@ -829,13 +842,13 @@ msgstr "Öffentlicher Goodreads API Schlüssel"
#: cps/templates/config_edit.html:119 #: cps/templates/config_edit.html:119
msgid "Goodreads API Secret" msgid "Goodreads API Secret"
msgstr Geheimer Goodreads API Schlüssel" msgstr "eheimer Goodreads API Schlüssel"
#: cps/templates/config_edit.html:125 #: cps/templates/config_edit.html:125
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "Default Einstellungen für neue Benutzer" 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" msgid "Admin user"
msgstr "Admin Benutzer" msgstr "Admin Benutzer"
@ -938,6 +951,11 @@ msgstr "Einstellungen speichern und Test E-Mail versenden"
msgid "Next" msgid "Next"
msgstr "Nächste" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "Entdecke (Zufälliges Buch)" msgstr "Entdecke (Zufälliges Buch)"
@ -946,52 +964,47 @@ msgstr "Entdecke (Zufälliges Buch)"
msgid "Start" msgid "Start"
msgstr "Start" msgstr "Start"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "Suche"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "Beliebte Bücher" msgstr "Beliebte Bücher"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "Beliebte Publikationen aus dieser Bibliothek basierend auf Downloadzahlen" 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" msgid "Best rated Books"
msgstr "Best bewertete Bücher" msgstr "Best bewertete Bücher"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "Beliebte Veröffentlichungen dieses Katalogs basierend auf Bewertungen" msgstr "Beliebte Veröffentlichungen dieses Katalogs basierend auf Bewertungen"
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "Neue Bücher" msgstr "Neue Bücher"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "Die neuesten Bücher" msgstr "Die neuesten Bücher"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "Zeige zufällige Bücher" 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" msgid "Authors"
msgstr "Autoren" msgstr "Autoren"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "Bücher nach Autoren sortiert" msgstr "Bücher nach Autoren sortiert"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "Bücher nach Kategorien sortiert" msgstr "Bücher nach Kategorien sortiert"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "Bücher nach Reihen geordnet" msgstr "Bücher nach Reihen geordnet"
@ -1102,7 +1115,11 @@ msgstr "Einloggen mit magischem Link"
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "Calibre Web Ebook Katalog" 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." msgid "Reflow text when sidebars are open."
msgstr "Text umbrechen wenn Seitenleiste geöffnet ist" msgstr "Text umbrechen wenn Seitenleiste geöffnet ist"
@ -1151,7 +1168,7 @@ msgid "No Results for:"
msgstr "Keine Ergebnisse für:" msgstr "Keine Ergebnisse für:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "Versuche eine andere Suche" msgstr "Versuche eine andere Suche"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1243,45 +1260,45 @@ msgid "Show all"
msgstr "Zeige alle" msgstr "Zeige alle"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr "Erwachsenencontent anzeigen"
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "Zeige Zufällige Bücher" msgstr "Zeige Zufällige Bücher"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "Zeige Auswahl Beliebte Bücher" msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "Zeige am besten bewertete Bücher" msgstr "Zeige am besten bewertete Bücher"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "Zeige Sprachauswahl" msgstr "Zeige Sprachauswahl"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "Zeige Serienauswahl" msgstr "Zeige Serienauswahl"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "Zeige Kategorienauswahl" msgstr "Zeige Kategorienauswahl"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "Zeige Autorenauswahl" msgstr "Zeige Autorenauswahl"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "Zeige Gelesen/Ungelesen Auswahl" msgstr "Zeige Gelesen/Ungelesen Auswahl"
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:78
msgid "Show random books in detail view" msgid "Show random books in detail view"
msgstr "Zeige zufällige Bücher in der Detailansicht" 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 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "Benutzer löschen" msgstr "Benutzer löschen"

@ -14,7 +14,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-web\n" "Project-Id-Version: Calibre-web\n"
"Report-Msgid-Bugs-To: https://github.com/janeczku/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" "PO-Revision-Date: 2017-04-04 15:09+0200\n"
"Last-Translator: Juan F. Villa <juan.villa@paisdelconocimiento.org>\n" "Last-Translator: Juan F. Villa <juan.villa@paisdelconocimiento.org>\n"
"Language: es\n" "Language: es\n"
@ -25,7 +25,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "No instalado" msgstr "No instalado"
@ -69,375 +69,375 @@ msgstr "Enviar a Kindle"
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "Formato no compatible para enviar por correo electronico" msgstr "Formato no compatible para enviar por correo electronico"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "Invitado" msgstr "Invitado"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Solicitando paquete de actualización" msgstr "Solicitando paquete de actualización"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Descargando paquete de actualización" msgstr "Descargando paquete de actualización"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Descomprimiendo paquete de actualización" msgstr "Descomprimiendo paquete de actualización"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "Ficheros sustituidos" msgstr "Ficheros sustituidos"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Los conexiones de base datos están cerradas" msgstr "Los conexiones de base datos están cerradas"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "El servidor está detenido" msgstr "El servidor está detenido"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Actualización finalizada. Por favor, pulse OK y recargue la página" 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" msgid "Recently Added Books"
msgstr "" msgstr ""
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "" msgstr ""
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "" msgstr ""
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "" msgstr ""
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "" msgstr ""
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Libros populares (los mas descargados)" msgstr "Libros populares (los mas descargados)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "Libros mejor valorados" 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" msgid "Random Books"
msgstr "Libros al azar" msgstr "Libros al azar"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "Lista de autores" 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:" 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:" 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" msgid "Series list"
msgstr "Lista de series" msgstr "Lista de series"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Series : %(serie)s" msgstr "Series : %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "Lenguajes disponibles" msgstr "Lenguajes disponibles"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Lenguaje: %(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" msgid "Category list"
msgstr "Lista de categorias" msgstr "Lista de categorias"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Categoría : %(name)s" msgstr "Categoría : %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "" msgstr ""
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "Estadisticas" msgstr "Estadisticas"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Servidor reiniciado. Por favor, recargue la página" 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" msgid "Performing shutdown of server, please close window"
msgstr "Servidor en proceso de apagado. Por favor, cierre la ventana." msgstr "Servidor en proceso de apagado. Por favor, cierre la ventana."
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "Actualización realizada" msgstr "Actualización realizada"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "búsqueda" msgstr "búsqueda"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "Libros leídos" msgstr "Libros leídos"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "Libros no leídos" 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" msgid "Read a Book"
msgstr "Leer un libro" 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!" msgid "Please fill out all fields!"
msgstr "¡Por favor completar todos los campos!" 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" msgid "register"
msgstr "registrarse" msgstr "registrarse"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Error desconocido. Por favor, inténtelo de nuevo mas tarde." 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." msgid "This username or email address is already in use."
msgstr "Usuario o dirección de correo en uso." 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 #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Sesion iniciada como : '%(nickname)s'" msgstr "Sesion iniciada como : '%(nickname)s'"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Usuario o contraseña invalido" msgstr "Usuario o contraseña invalido"
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "Iniciar sesión" msgstr "Iniciar sesión"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "" msgstr ""
#: cps/web.py:1980 cps/web.py:2011 #: cps/web.py:1960 cps/web.py:1991
msgid "Token has expired" msgid "Token has expired"
msgstr "" msgstr ""
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "" msgstr ""
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Configurar primero los parametros SMTP por favor..." msgstr "Configurar primero los parametros SMTP por favor..."
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Envio de Libro a %(kindlemail)s correctamente" msgstr "Envio de Libro a %(kindlemail)s correctamente"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Ha sucedido un error en el envio del Libro: %(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..." msgid "Please configure your kindle email address first..."
msgstr "Configurar primero la dirección de correo Kindle por favor..." msgstr "Configurar primero la dirección de correo Kindle por favor..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "El libro fue agregado a el estante: %(sname)s" msgstr "El libro fue agregado a el estante: %(sname)s"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "El libro fue removido del estante: %(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 #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Une étagère de ce nom '%(title)s' existe déjà." msgstr "Une étagère de ce nom '%(title)s' existe déjà."
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Estante %(title)s creado" 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" msgid "There was an error"
msgstr "Ha sucedido un 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" msgid "create a shelf"
msgstr "crear un estante" msgstr "crear un estante"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "Estante %(title)s cambiado" 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" msgid "Edit a shelf"
msgstr "Editar un estante" msgstr "Editar un estante"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "Estante %(name)s fue borrado correctamente" msgstr "Estante %(name)s fue borrado correctamente"
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Estante: '%(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" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "" msgstr ""
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "Cambiar orden del estante: '%(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." msgid "Found an existing account for this email address."
msgstr "Existe una cuenta vinculada a esta dirección de correo." 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 #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "Perfil de %(name)s" msgstr "Perfil de %(name)s"
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "Perfil actualizado" msgstr "Perfil actualizado"
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "Página de administración" msgstr "Página de administración"
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Configuración de Calibre-web actualizada" 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" msgid "Basic Configuration"
msgstr "Configuración básica" msgstr "Configuración básica"
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "Localicación de la BD inválida. Por favor, introduzca la ruta correcta." 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" msgid "Add new user"
msgstr "Agregar un nuevo usuario" msgstr "Agregar un nuevo usuario"
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Usuario '%(user)s' creado" msgstr "Usuario '%(user)s' creado"
#: cps/web.py:2564 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "" msgstr ""
"Se ha encontrado una cuenta vinculada a esta dirección de correo o nombre" "Se ha encontrado una cuenta vinculada a esta dirección de correo o nombre"
" de usuario." " de usuario."
#: cps/web.py:2586 #: cps/web.py:2566
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "Parámetros de correo actualizados" msgstr "Parámetros de correo actualizados"
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Exito al realizar envio de prueba a %(kindlemail)s" msgstr "Exito al realizar envio de prueba a %(kindlemail)s"
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "Error al realizar envio de prueba a 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" msgid "E-Mail settings updated"
msgstr "Ajustes de correo electrónico actualizados" msgstr "Ajustes de correo electrónico actualizados"
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "Editar parametros de correo" msgstr "Editar parametros de correo"
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Usuario '%(nick)s' borrado" msgstr "Usuario '%(nick)s' borrado"
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Usuario '%(nick)s' actualizado" msgstr "Usuario '%(nick)s' actualizado"
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Error inesperado." msgstr "Error inesperado."
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Editar Usuario %(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" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "" 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" msgid "edit metadata"
msgstr "editar metainformación" msgstr "editar metainformación"
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "" msgstr ""
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" 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" 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" msgid "File to be uploaded must have an extension"
msgstr "El archivo a subir debe tener una extensión" msgstr "El archivo a subir debe tener una extensión"
#: cps/web.py:2997 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Fallo al crear la ruta %s (permiso negado)" msgstr "Fallo al crear la ruta %s (permiso negado)"
#: cps/web.py:3002 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Fallo al almacenar el archivo %s (permiso negado)" msgstr "Fallo al almacenar el archivo %s (permiso negado)"
#: cps/web.py:3007 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Fallo al borrar el archivo %s (permiso negado)" msgstr "Fallo al borrar el archivo %s (permiso negado)"
@ -475,6 +475,10 @@ msgstr "Descarga"
msgid "Upload" msgid "Upload"
msgstr "Subir archivo" msgstr "Subir archivo"
#: cps/templates/detail.html:14
msgid "Upload format"
msgstr "Subir formato"
#: cps/templates/admin.html:15 #: cps/templates/admin.html:15
msgid "Edit" msgid "Edit"
msgstr "Editar" msgstr "Editar"
@ -608,6 +612,18 @@ msgstr "¿Seguro que quiere detener Calibre-web?"
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "Actualizando. Por favor, no recargue la página." 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "" msgstr ""
@ -616,12 +632,13 @@ msgstr ""
msgid "Book Title" msgid "Book Title"
msgstr "Titulo del Libro" msgstr "Titulo del Libro"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "Autor" 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" msgid "Description"
msgstr "Descripcion" msgstr "Descripcion"
@ -704,35 +721,35 @@ msgstr "Buscar palabras clave"
msgid "Go!" msgid "Go!"
msgstr "¡Vamos!" msgstr "¡Vamos!"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "Haga clic en la portada para cargar la metainformación en el formulario" 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..." msgid "Loading..."
msgstr "Cargando..." 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" msgid "Close"
msgstr "Cerrar" msgstr "Cerrar"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "Editor" msgstr "Editor"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "Origen" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Ubicación de la base de datos Calibre" msgstr "Ubicación de la base de datos Calibre"
@ -822,7 +839,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "Ajustes por defecto para nuevos usuarios" 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" msgid "Admin user"
msgstr "Usuario Administrador" msgstr "Usuario Administrador"
@ -925,6 +942,11 @@ msgstr "Guardar cambios y enviar un correo de prueba"
msgid "Next" msgid "Next"
msgstr "Siguiente" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "Descubrir (Libros al azar)" msgstr "Descubrir (Libros al azar)"
@ -933,52 +955,47 @@ msgstr "Descubrir (Libros al azar)"
msgid "Start" msgid "Start"
msgstr "Iniciar" msgstr "Iniciar"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "Buscar"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "Libros Populares" msgstr "Libros Populares"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "Publicaciones mas populares para este catálogo basadas en las descargas." 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" msgid "Best rated Books"
msgstr "Libros mejor valorados" msgstr "Libros mejor valorados"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "Publicaciones populares del catalogo basados en el puntaje." msgstr "Publicaciones populares del catalogo basados en el puntaje."
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "Nuevos libros" msgstr "Nuevos libros"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "Libros recientes" msgstr "Libros recientes"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "Mostrar libros al azar" 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" msgid "Authors"
msgstr "Autores" msgstr "Autores"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "Libros ordenados por Autor" msgstr "Libros ordenados por Autor"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "Libros ordenados por Categorias" msgstr "Libros ordenados por Categorias"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "Libros ordenados por Series" msgstr "Libros ordenados por Series"
@ -1089,7 +1106,11 @@ msgstr ""
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "Catálogo de libros electrónicos Calibre Web" 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." msgid "Reflow text when sidebars are open."
msgstr "Redimensionar el texto cuando las barras laterales estan abiertas" msgstr "Redimensionar el texto cuando las barras laterales estan abiertas"
@ -1138,7 +1159,7 @@ msgid "No Results for:"
msgstr "Sin resultados para:" msgstr "Sin resultados para:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "Intente una busqueda diferente" msgstr "Intente una busqueda diferente"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1230,45 +1251,45 @@ msgid "Show all"
msgstr "Mostrar Todo" msgstr "Mostrar Todo"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "Mostrar libros al azar" msgstr "Mostrar libros al azar"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "Mostrar libros populares" msgstr "Mostrar libros populares"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "Mostrar libros mejor valorados" msgstr "Mostrar libros mejor valorados"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "Mostrar lenguaje seleccionado" msgstr "Mostrar lenguaje seleccionado"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "Mostrar series seleccionadas" msgstr "Mostrar series seleccionadas"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "Mostrar categorias elegidas" msgstr "Mostrar categorias elegidas"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "Mostrar selección de autores" msgstr "Mostrar selección de autores"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "Mostrar leídos y no leídos" 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" msgid "Show random books in detail view"
msgstr "Mostrar libro aleatorios con vista detallada" msgstr "Mostrar libro aleatorios con vista detallada"
#: cps/templates/user_edit.html:90
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:123 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "Borrar este usuario" msgstr "Borrar este usuario"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -22,7 +22,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-web dutch translation by Ed Driesen (GPL V3)\n" "Project-Id-Version: Calibre-web dutch translation by Ed Driesen (GPL V3)\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: 2017-06-21 20:15+0200\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language: nl\n" "Language: nl\n"
@ -33,7 +33,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "niet geïnstalleerd" msgstr "niet geïnstalleerd"
@ -77,382 +77,384 @@ msgstr "Stuur naar Kindle:"
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "Kon geen geschikte formaten vinden om te verzenden per email" msgstr "Kon geen geschikte formaten vinden om te verzenden per email"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "Gast" msgstr "Gast"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Update pakket wordt aangevraagd" msgstr "Update pakket wordt aangevraagd"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Update pakket wordt gedownload" msgstr "Update pakket wordt gedownload"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Update pakket wordt uitgepakt" msgstr "Update pakket wordt uitgepakt"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "Bestanden zijn vervangen" msgstr "Bestanden zijn vervangen"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Database verbindingen zijn gesloten" msgstr "Database verbindingen zijn gesloten"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "Server is gestopt" msgstr "Server is gestopt"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Update voltooid, klik op ok en herlaad de pagina" msgstr "Update voltooid, klik op ok en herlaad de pagina"
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "" msgstr "Recent toegevoegde boeken"
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "" msgstr "Nieuwste boeken"
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "" msgstr "Oudste boeken"
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "" msgstr "Boeken (A-Z)"
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "" msgstr "Boeken (A-Z)"
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Populaire boeken (meeste downloads)" msgstr "Populaire boeken (meeste downloads)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "Best beoordeelde boeken" 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" msgid "Random Books"
msgstr "Willekeurige boeken" msgstr "Willekeurige boeken"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "Auteur lijst" 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:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "" msgstr ""
"Fout bij openen van het boek. Bestand bestaat niet of is niet " "Fout bij openen van het boek. Bestand bestaat niet of is niet "
"toegankelijk:" "toegankelijk:"
#: cps/templates/index.xml:71 cps/web.py:1198 #: cps/templates/index.xml:70 cps/web.py:1176
msgid "Series list" msgid "Series list"
msgstr "Series lijst" msgstr "Serie lijst"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Series: %(serie)s" msgstr "Serie: %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "Beschikbare talen" msgstr "Beschikbare talen"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Taal: %(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" msgid "Category list"
msgstr "Categorie lijst" msgstr "Categorie lijst"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Categorie: %(name)s" msgstr "Categorie: %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "" msgstr ""
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "Statistieken" msgstr "Statistieken"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Server herstart, gelieve de pagina herladen" msgstr "Server herstart, gelieve de pagina herladen"
#: cps/web.py:1565 #: cps/web.py:1549
msgid "Performing shutdown of server, please close window" 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" msgid "Update done"
msgstr "Update voltooid" msgstr "Update voltooid"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "zoek" msgstr "zoek"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "Lees Boeken" msgstr "Gelezen Boeken"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "Ongelezen Boeken" 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" msgid "Read a Book"
msgstr "Lees een boek" 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!" msgid "Please fill out all fields!"
msgstr "Gelieve alle velden in te vullen!" 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" msgid "register"
msgstr "registreer" msgstr "registreer"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Een onbekende fout deed zich voor. Gelieve later nog eens te proberen." 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." 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 #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "je bent nu ingelogd als: '%(nickname)s'" msgstr "je bent nu ingelogd als: '%(nickname)s'"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Verkeerde gebruikersnaam of Wachtwoord" msgstr "Verkeerde gebruikersnaam of Wachtwoord"
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "login" msgstr "login"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" 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" msgid "Token has expired"
msgstr "" msgstr "Token is verlopen"
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" 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..." 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 #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Boek met succes verstuurd naar %(kindlemail)s" msgstr "Boek met succes verstuurd naar %(kindlemail)s"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Er trad een fout op bij het versturen van dit boek: %(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..." msgid "Please configure your kindle email address first..."
msgstr "Gelieve eerst je kindle email adres te configureren..." msgstr "Gelieve eerst je kindle email adres te configureren..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Boek werd toegevoegd aan boekenplank: %(sname)s" msgstr "Boek werd toegevoegd aan boekenplank: %(sname)s"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Boek werd verwijderd van boekenplank: %(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 #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Een boekenplank met de naam '%(title)s' bestaat reeds." msgstr "Een boekenplank met de naam '%(title)s' bestaat reeds."
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Boekenplank %(title)s aangemaakt" 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" msgid "There was an error"
msgstr "Er deed zich een fout voor" 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" msgid "create a shelf"
msgstr "maak een boekenplank" msgstr "maak een boekenplank"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "Boekenplank %(title)s gewijzigd" 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" msgid "Edit a shelf"
msgstr "Bewerk een boekenplank" msgstr "Bewerk een boekenplank"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" 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 #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Boekenplank: '%(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" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "" msgstr ""
"Fout bij openen boekenplank. Boekenplank bestaat niet of is niet "
"toegankelijk"
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "Verander volgorde van Boekenplank: '%(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." msgid "Found an existing account for this email address."
msgstr "Een bestaand gebruiker gevonden voor dit email adres." 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 #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "%(name)s's profiel" msgstr "%(name)s's profiel"
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "Profiel aangepast" msgstr "Profiel aangepast"
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "Administratie pagina" msgstr "Administratie pagina"
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Calibre-web configuratie aangepast" 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" msgid "Basic Configuration"
msgstr "Basis configuratie" msgstr "Basis configuratie"
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "DB locatie is niet geldig, gelieve het correcte pad in te geven" 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" msgid "Add new user"
msgstr "Voeg nieuwe gebruiker toe" msgstr "Voeg nieuwe gebruiker toe"
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Gebruiker '%(user)s' aangemaakt" msgstr "Gebruiker '%(user)s' aangemaakt"
#: cps/web.py:2564 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." 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" msgid "Mail settings updated"
msgstr "Mail instellingen aangepast" msgstr "Mail instellingen aangepast"
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Test email met succes verstuurd naar %(kindlemail)s" msgstr "Test email met succes verstuurd naar %(kindlemail)s"
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" 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" 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" msgid "E-Mail settings updated"
msgstr "Email instellingen aangepast" msgstr "Email instellingen aangepast"
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "Bewerk mail instellingen" msgstr "Bewerk mail instellingen"
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Gebruiker '%(nick)s' verwijderd" msgstr "Gebruiker '%(nick)s' verwijderd"
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Gebruiker '%(nick)s' aangepast" msgstr "Gebruiker '%(nick)s' aangepast"
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Een onbekende fout deed zich voor." msgstr "Een onbekende fout deed zich voor."
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Bewerk gebruiker '%(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" 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" msgid "edit metadata"
msgstr "Bewerk metadata" msgstr "Bewerk metadata"
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "onbekend" msgstr "onbekend"
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" 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" 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" 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 #, python-format
msgid "Failed to create path %s (Permission denied)." 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 #, python-format
msgid "Failed to store file %s (Permission denied)." 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 #, python-format
msgid "Failed to delete file %s (Permission denied)." 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 #: cps/templates/admin.html:4
msgid "User list" msgid "User list"
msgstr "Gebruikers lijst" msgstr "Gebruikerslijst"
#: cps/templates/admin.html:8 #: cps/templates/admin.html:8
msgid "Nickname" msgid "Nickname"
@ -557,7 +559,7 @@ msgstr "Anoniem verkennen"
#: cps/templates/admin.html:67 cps/templates/remote_login.html:4 #: cps/templates/admin.html:67 cps/templates/remote_login.html:4
msgid "Remote Login" msgid "Remote Login"
msgstr "" msgstr "Login op afstand"
#: cps/templates/admin.html:80 #: cps/templates/admin.html:80
msgid "Administration" msgid "Administration"
@ -616,6 +618,18 @@ msgstr "Wil je Calibre-web echt stoppen?"
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "Aan het updaten, gelieve de pagina niet te herladen" 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "Wis boek" msgstr "Wis boek"
@ -624,12 +638,13 @@ msgstr "Wis boek"
msgid "Book Title" msgid "Book Title"
msgstr "Boek titel" msgstr "Boek titel"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "Auteur" 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" msgid "Description"
msgstr "Omschrijving" msgstr "Omschrijving"
@ -712,35 +727,35 @@ msgstr "Zoek voor zoekwoord"
msgid "Go!" msgid "Go!"
msgstr "Start!" msgstr "Start!"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "Klik op de omslag om de metatadata in het formulier te laden" 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..." msgid "Loading..."
msgstr "Aan het laden..." 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" msgid "Close"
msgstr "Sluit" msgstr "Sluit"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "Uitgever" msgstr "Uitgever"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "Bron" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Locatie van de Calibre database" msgstr "Locatie van de Calibre database"
@ -830,7 +845,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "Standaard instellingen voor nieuwe gebruikers" 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" msgid "Admin user"
msgstr "Administratie gebruiker" msgstr "Administratie gebruiker"
@ -933,6 +948,11 @@ msgstr "Bewaar instellingen en stuur test email"
msgid "Next" msgid "Next"
msgstr "Volgende" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "Ontdek (Willekeurige Boeken)" msgstr "Ontdek (Willekeurige Boeken)"
@ -941,52 +961,47 @@ msgstr "Ontdek (Willekeurige Boeken)"
msgid "Start" msgid "Start"
msgstr "Start" msgstr "Start"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "Zoek"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "Populaire Boeken" msgstr "Populaire Boeken"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "Populaire publicaties van deze cataloog gebaseerd op 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" msgid "Best rated Books"
msgstr "Best beoordeeld" msgstr "Best beoordeeld"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "Populaire publicaties van deze cataloog gebaseerd op Beoordeling." msgstr "Populaire publicaties van deze cataloog gebaseerd op Beoordeling."
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "Nieuwe Boeken" msgstr "Nieuwe Boeken"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "Recentste boeken" msgstr "Recentste boeken"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "Toon Willekeurige Boeken" 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" msgid "Authors"
msgstr "Auteurs" msgstr "Auteurs"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "Boeken gesorteerd op Auteur" msgstr "Boeken gesorteerd op Auteur"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "Boeken gesorteerd op Categorie" msgstr "Boeken gesorteerd op Categorie"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "Boeken gesorteerd op Serie" msgstr "Boeken gesorteerd op Serie"
@ -1097,7 +1112,11 @@ msgstr ""
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "Calible web ebook cataloog" 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." msgid "Reflow text when sidebars are open."
msgstr "Herschuif tekst waneer het zijpaneel open staat." msgstr "Herschuif tekst waneer het zijpaneel open staat."
@ -1146,7 +1165,7 @@ msgid "No Results for:"
msgstr "Geen resultaat voor:" msgstr "Geen resultaat voor:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "Gelieve een ander zoekwoord proberen" msgstr "Gelieve een ander zoekwoord proberen"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1238,45 +1257,45 @@ msgid "Show all"
msgstr "Toon alles" msgstr "Toon alles"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "Toon willekeurige boeken" msgstr "Toon willekeurige boeken"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "Toon populaire boeken" msgstr "Toon populaire boeken"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "Toon best beoordeelde boeken" msgstr "Toon best beoordeelde boeken"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "Toon taal selectie" msgstr "Toon taal selectie"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "Toon serie selectie" msgstr "Toon serie selectie"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "Toon categorie selectie" msgstr "Toon categorie selectie"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "Toon auteur selectie" msgstr "Toon auteur selectie"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "Toon gelezen en ongelezen" msgstr "Toon gelezen en ongelezen"
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:78
msgid "Show random books in detail view" msgid "Show random books in detail view"
msgstr "Toon willekeurige boeken in gedetailleerd zicht" msgstr "Toon willekeurige boeken in gedetailleerd zicht"
#: cps/templates/user_edit.html:90
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:123 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "Wis deze gebruiker" msgstr "Wis deze gebruiker"

@ -12,7 +12,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre Web - polski (POT: 2017-04-11 22:51)\n" "Project-Id-Version: Calibre Web - polski (POT: 2017-04-11 22:51)\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: 2017-04-11 22:51+0200\n"
"Last-Translator: Radosław Kierznowski <radek.kierznowski@outlook.com>\n" "Last-Translator: Radosław Kierznowski <radek.kierznowski@outlook.com>\n"
"Language: pl\n" "Language: pl\n"
@ -24,7 +24,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "nie zainstalowane" msgstr "nie zainstalowane"
@ -70,373 +70,373 @@ msgstr ""
"Nie można znaleźć żadnych formatów przystosowane do wysyłania pocztą " "Nie można znaleźć żadnych formatów przystosowane do wysyłania pocztą "
"e-mail" "e-mail"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "Gość" msgstr "Gość"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Żądanie o pakiet aktualizacji" msgstr "Żądanie o pakiet aktualizacji"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Pobieranie pakietu aktualizacji" msgstr "Pobieranie pakietu aktualizacji"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Rozpakowywanie pakietu aktualizacji" msgstr "Rozpakowywanie pakietu aktualizacji"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "Pliki zostały zastąpione" msgstr "Pliki zostały zastąpione"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Połączenia z bazą danych zostały zakończone" msgstr "Połączenia z bazą danych zostały zakończone"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "Serwer jest zatrzymany" msgstr "Serwer jest zatrzymany"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Aktualizacja zakończona, proszę nacisnąć OK i odświeżyć stronę" msgstr "Aktualizacja zakończona, proszę nacisnąć OK i odświeżyć stronę"
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "" msgstr ""
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "" msgstr ""
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "" msgstr ""
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "" msgstr ""
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "" msgstr ""
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Najpopularniejsze książki (najczęściej pobierane)" msgstr "Najpopularniejsze książki (najczęściej pobierane)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "Najlepiej oceniane książki" 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" msgid "Random Books"
msgstr "Losowe książki" msgstr "Losowe książki"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "Lista autorów" 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:" 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:" 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" msgid "Series list"
msgstr "Lista serii" msgstr "Lista serii"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Seria: %(serie)s" msgstr "Seria: %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "Dostępne języki" msgstr "Dostępne języki"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Język: %(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" msgid "Category list"
msgstr "Lista kategorii" msgstr "Lista kategorii"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Kategoria: %(name)s" msgstr "Kategoria: %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "" msgstr ""
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "Statystyki" msgstr "Statystyki"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Serwer uruchomiony ponownie, proszę odświeżyć stronę" 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" msgid "Performing shutdown of server, please close window"
msgstr "Wykonano wyłączenie serwera, proszę zamknąć okno" msgstr "Wykonano wyłączenie serwera, proszę zamknąć okno"
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "Aktualizacja zakończona" msgstr "Aktualizacja zakończona"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "szukaj" msgstr "szukaj"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "Przeczytane książki" msgstr "Przeczytane książki"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "Nieprzeczytane książki" 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" msgid "Read a Book"
msgstr "Czytaj książkę" 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!" msgid "Please fill out all fields!"
msgstr "Proszę wypełnić wszystkie pola!" 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" msgid "register"
msgstr "rejestracja" msgstr "rejestracja"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Wystąpił nieznany błąd. Spróbuj ponownie później." 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." msgid "This username or email address is already in use."
msgstr "Nazwa użytkownika lub adres e-mail jest już w użyciu." 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 #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Zalogowałeś się jako: '%(nickname)s'" msgstr "Zalogowałeś się jako: '%(nickname)s'"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Błędna nazwa użytkownika lub hasło" 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" msgid "login"
msgstr "logowanie" msgstr "logowanie"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "" msgstr ""
#: cps/web.py:1980 cps/web.py:2011 #: cps/web.py:1960 cps/web.py:1991
msgid "Token has expired" msgid "Token has expired"
msgstr "" msgstr ""
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "" msgstr ""
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Proszę najpierw skonfigurować ustawienia SMTP poczty e-mail..." msgstr "Proszę najpierw skonfigurować ustawienia SMTP poczty e-mail..."
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Książka została pomyślnie wysłana do %(kindlemail)s" msgstr "Książka została pomyślnie wysłana do %(kindlemail)s"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Wystąpił błąd podczas wysyłania tej książki: %(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..." msgid "Please configure your kindle email address first..."
msgstr "Proszę najpierw skonfigurować adres e-mail swojego kindla..." msgstr "Proszę najpierw skonfigurować adres e-mail swojego kindla..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Książka została dodana do półki: %(sname)s" msgstr "Książka została dodana do półki: %(sname)s"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Książka została usunięta z półki: %(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 #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Półka o nazwie '%(title)s' już istnieje." msgstr "Półka o nazwie '%(title)s' już istnieje."
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Półka %(title)s została utworzona" 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" msgid "There was an error"
msgstr "Wystąpił błąd" 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" msgid "create a shelf"
msgstr "utwórz półkę" msgstr "utwórz półkę"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "Półka %(title)s została zmieniona" 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" msgid "Edit a shelf"
msgstr "Edytuj półkę" msgstr "Edytuj półkę"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "pomyślnie usunięto półkę %(name)s" msgstr "pomyślnie usunięto półkę %(name)s"
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Półka: '%(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" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "" msgstr ""
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "Zmieniono kolejność półki: '%(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." msgid "Found an existing account for this email address."
msgstr "Znaleziono istniejące konto dla tego adresu e-mail." 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 #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "Profil użytkownika %(name)s" msgstr "Profil użytkownika %(name)s"
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "Zaktualizowano profil" msgstr "Zaktualizowano profil"
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "Portal administracyjny" msgstr "Portal administracyjny"
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Konfiguracja Calibre-web została zaktualizowana" 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" msgid "Basic Configuration"
msgstr "Podstawowa konfiguracja" msgstr "Podstawowa konfiguracja"
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "Lokalizacja bazy danych jest nieprawidłowa, wpisz poprawną ścieżkę" 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" msgid "Add new user"
msgstr "Dodaj nowego użytkownika" msgstr "Dodaj nowego użytkownika"
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Użytkownik '%(user)s' został utworzony" 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." 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." 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" msgid "Mail settings updated"
msgstr "Zaktualizowano ustawienia poczty e-mail" msgstr "Zaktualizowano ustawienia poczty e-mail"
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Testowy e-mail został pomyślnie wysłany do %(kindlemail)s" msgstr "Testowy e-mail został pomyślnie wysłany do %(kindlemail)s"
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" 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" 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" msgid "E-Mail settings updated"
msgstr "Zaktualizowano ustawienia e-mail" msgstr "Zaktualizowano ustawienia e-mail"
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "Edytuj ustawienia poczty e-mail" msgstr "Edytuj ustawienia poczty e-mail"
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Użytkownik '%(nick)s' został usunięty" msgstr "Użytkownik '%(nick)s' został usunięty"
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Użytkownik '%(nick)s' został zaktualizowany" msgstr "Użytkownik '%(nick)s' został zaktualizowany"
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Wystąpił nieznany błąd." msgstr "Wystąpił nieznany błąd."
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Edytuj użytkownika %(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" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "" 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" msgid "edit metadata"
msgstr "edytuj metadane" msgstr "edytuj metadane"
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "" msgstr ""
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" 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" 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" msgid "File to be uploaded must have an extension"
msgstr "Plik do przesłania musi mieć rozszerzenie" msgstr "Plik do przesłania musi mieć rozszerzenie"
#: cps/web.py:2997 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Nie udało się utworzyć łącza %s (Odmowa dostępu)." msgstr "Nie udało się utworzyć łącza %s (Odmowa dostępu)."
#: cps/web.py:3002 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Nie można przechowywać pliku %s (Odmowa dostępu)." msgstr "Nie można przechowywać pliku %s (Odmowa dostępu)."
#: cps/web.py:3007 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Nie udało się usunąć pliku %s (Odmowa dostępu)." 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" msgid "Updating, please do not reload page"
msgstr "Aktualizowanie, proszę nie odświeżać strony" 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "" msgstr ""
@ -615,12 +627,13 @@ msgstr ""
msgid "Book Title" msgid "Book Title"
msgstr "Tytuł książki" msgstr "Tytuł książki"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "Autor" 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" msgid "Description"
msgstr "Opis" msgstr "Opis"
@ -703,35 +716,35 @@ msgstr " Szukaj słowa kluczowego "
msgid "Go!" msgid "Go!"
msgstr "Idź!" msgstr "Idź!"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "Kliknij okładkę, aby załadować metadane do formularza" 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..." msgid "Loading..."
msgstr "Ładowanie..." 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" msgid "Close"
msgstr "Zamknij" msgstr "Zamknij"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "Wydawca" msgstr "Wydawca"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "Źródło" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Lokalizacja bazy danych Calibre" msgstr "Lokalizacja bazy danych Calibre"
@ -822,7 +835,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "Domyślne ustawienia dla nowych użytkowników" 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" msgid "Admin user"
msgstr "Użytkownik z uprawnieniami administratora" msgstr "Użytkownik z uprawnieniami administratora"
@ -925,6 +938,11 @@ msgstr "Zapisz ustawienia i wyślij testową wiadomość e-mail"
msgid "Next" msgid "Next"
msgstr "Następne" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "Odkrywaj (losowe książki)" msgstr "Odkrywaj (losowe książki)"
@ -933,52 +951,47 @@ msgstr "Odkrywaj (losowe książki)"
msgid "Start" msgid "Start"
msgstr "Rozpocznij" msgstr "Rozpocznij"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "Szukaj"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "Najpopularniejsze książki" msgstr "Najpopularniejsze książki"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "Popularne publikacje z tego katalogu bazujące na pobranych." 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" msgid "Best rated Books"
msgstr "Najlepiej ocenione książki" msgstr "Najlepiej ocenione książki"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "Popularne publikacje z tego katalogu bazujące na ocenach." msgstr "Popularne publikacje z tego katalogu bazujące na ocenach."
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "Nowe książki" msgstr "Nowe książki"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "Ostatnie książki" msgstr "Ostatnie książki"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "Pokazuj losowe książki" 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" msgid "Authors"
msgstr "Autorzy" msgstr "Autorzy"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "Książki sortowane według autorów" msgstr "Książki sortowane według autorów"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "Książki sortowane według kategorii" msgstr "Książki sortowane według kategorii"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "Książki sortowane według serii" msgstr "Książki sortowane według serii"
@ -1089,7 +1102,11 @@ msgstr ""
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "Calibre Web katalog ebooków" 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 #, fuzzy
msgid "Reflow text when sidebars are open." msgid "Reflow text when sidebars are open."
msgstr "Tekst pływający, gdy paski boczne są otwarte." msgstr "Tekst pływający, gdy paski boczne są otwarte."
@ -1139,7 +1156,7 @@ msgid "No Results for:"
msgstr "Brak wyników dla:" msgstr "Brak wyników dla:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "Proszę wypróbować podobne wyszukiwanie" msgstr "Proszę wypróbować podobne wyszukiwanie"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1231,45 +1248,45 @@ msgid "Show all"
msgstr "Pokaż wszystko" msgstr "Pokaż wszystko"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "Pokaż losowe książki" msgstr "Pokaż losowe książki"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "Pokaż najpopularniejsze książki" msgstr "Pokaż najpopularniejsze książki"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "Pokaż najlepiej ocenione książki" msgstr "Pokaż najlepiej ocenione książki"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "Pokaż wybór języka" msgstr "Pokaż wybór języka"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "Pokaż wybór serii" msgstr "Pokaż wybór serii"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "Pokaż wybór kategorii" msgstr "Pokaż wybór kategorii"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "Pokaż wybór autora" msgstr "Pokaż wybór autora"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "Pokaż przeczytane i nieprzeczytane" msgstr "Pokaż przeczytane i nieprzeczytane"
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:78
msgid "Show random books in detail view" msgid "Show random books in detail view"
msgstr "Pokaz losowe książki w widoku szczegółowym" 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 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "Usuń tego użytkownika" msgstr "Usuń tego użytkownika"

@ -14,7 +14,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-web\n" "Project-Id-Version: Calibre-web\n"
"Report-Msgid-Bugs-To: https://github.com/janeczku/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" "PO-Revision-Date: 2017-04-30 00:47+0300\n"
"Last-Translator: Pavel Korovin <p@tristero.se>\n" "Last-Translator: Pavel Korovin <p@tristero.se>\n"
"Language: ru\n" "Language: ru\n"
@ -26,7 +26,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "Отсутствует" msgstr "Отсутствует"
@ -70,373 +70,373 @@ msgstr "Отправить на Kindle"
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "Невозоможно найти формат, подходящий для отправки по email" msgstr "Невозоможно найти формат, подходящий для отправки по email"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "Гость" msgstr "Гость"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "Проверка обновлений" msgstr "Проверка обновлений"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "Загрузка обновлений" msgstr "Загрузка обновлений"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "Распаковка обновлений" msgstr "Распаковка обновлений"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "Файлы заменены" msgstr "Файлы заменены"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "Соеднинения с базой данных закрыты" msgstr "Соеднинения с базой данных закрыты"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "Сервер остановлен" msgstr "Сервер остановлен"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "Обновления установлены, нажмите okay и перезагрузите страницу" msgstr "Обновления установлены, нажмите okay и перезагрузите страницу"
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "" msgstr ""
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "" msgstr ""
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "" msgstr ""
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "" msgstr ""
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "" msgstr ""
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "Популярные книги (часто загружаемые)" msgstr "Популярные книги (часто загружаемые)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "Книги с наивысшим рейтингом" msgstr "Книги с наивысшим рейтингом"
#: cps/templates/index.xml:36 cps/web.py:1145 #: cps/templates/index.xml:35 cps/web.py:1115
msgid "Random Books" msgid "Random Books"
msgstr "Случайный выбор" msgstr "Случайный выбор"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "Авторы" 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:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "Невозможно открыть книгу. Файл не существует или недоступен." msgstr "Невозможно открыть книгу. Файл не существует или недоступен."
#: cps/templates/index.xml:71 cps/web.py:1198 #: cps/templates/index.xml:70 cps/web.py:1176
msgid "Series list" msgid "Series list"
msgstr "Серии" msgstr "Серии"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "Серии: %(serie)s" msgstr "Серии: %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "Языки" msgstr "Языки"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "Язык: %(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" msgid "Category list"
msgstr "Категории" msgstr "Категории"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "Категория: %(name)s" msgstr "Категория: %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "" msgstr ""
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "Статистика" msgstr "Статистика"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "Сервер перезагружен, пожалуйста, перезагрузите страницу" msgstr "Сервер перезагружен, пожалуйста, перезагрузите страницу"
#: cps/web.py:1565 #: cps/web.py:1549
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
msgstr "Производится остановка сервера, пожалуйста, закройте окно" msgstr "Производится остановка сервера, пожалуйста, закройте окно"
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "Обновление закончено" msgstr "Обновление закончено"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "поиск" msgstr "поиск"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "Прочитанные" msgstr "Прочитанные"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "Непрочитанные" 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" msgid "Read a Book"
msgstr "Читать книгу" msgstr "Читать книгу"
#: cps/web.py:1888 cps/web.py:2513 #: cps/web.py:1868 cps/web.py:2493
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "Пожалуйста, заполните все поля!" 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" msgid "register"
msgstr "зарегистрироваться" msgstr "зарегистрироваться"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "Неизвестная ошибка. Пожалуйста, попробуйте позже." msgstr "Неизвестная ошибка. Пожалуйста, попробуйте позже."
#: cps/web.py:1909 #: cps/web.py:1889
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "Имя пользователя или адрес эл. почты уже используется" msgstr "Имя пользователя или адрес эл. почты уже используется"
#: cps/web.py:1928 cps/web.py:2024 #: cps/web.py:1908 cps/web.py:2004
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "Вы вошли как пользователь '%(nickname)s'" msgstr "Вы вошли как пользователь '%(nickname)s'"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "Ошибка в имени пользователя или пароле" msgstr "Ошибка в имени пользователя или пароле"
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "войти" msgstr "войти"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "" msgstr ""
#: cps/web.py:1980 cps/web.py:2011 #: cps/web.py:1960 cps/web.py:1991
msgid "Token has expired" msgid "Token has expired"
msgstr "" msgstr ""
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "" msgstr ""
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "Пожалуйста, сначала сконфигурируйте параметры SMTP" msgstr "Пожалуйста, сначала сконфигурируйте параметры SMTP"
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "Книга успешно отправлена на %(kindlemail)s" msgstr "Книга успешно отправлена на %(kindlemail)s"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "Ошибка при отправке книги: %(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..." msgid "Please configure your kindle email address first..."
msgstr "Пожалуйста, сначала укажите ваш kindle email..." msgstr "Пожалуйста, сначала укажите ваш kindle email..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "Книга добавлена на книжную полку: %(sname)s" msgstr "Книга добавлена на книжную полку: %(sname)s"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "Книга удалена с книжной полки: %(sname)s" msgstr "Книга удалена с книжной полки: %(sname)s"
#: cps/web.py:2146 cps/web.py:2170 #: cps/web.py:2126 cps/web.py:2150
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "Книжкная полка с названием '%(title)s' уже существует." msgstr "Книжкная полка с названием '%(title)s' уже существует."
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "Создана книжная полка %(title)s" msgstr "Создана книжная полка %(title)s"
#: cps/web.py:2153 cps/web.py:2181 #: cps/web.py:2133 cps/web.py:2161
msgid "There was an error" msgid "There was an error"
msgstr "Произошла ошибка" msgstr "Произошла ошибка"
#: cps/web.py:2154 cps/web.py:2156 #: cps/web.py:2134 cps/web.py:2136
msgid "create a shelf" msgid "create a shelf"
msgstr "создать книжную полку" msgstr "создать книжную полку"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "Книжная полка %(title)s изменена" msgstr "Книжная полка %(title)s изменена"
#: cps/web.py:2182 cps/web.py:2184 #: cps/web.py:2162 cps/web.py:2164
msgid "Edit a shelf" msgid "Edit a shelf"
msgstr "Изменить книжную полку" msgstr "Изменить книжную полку"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "Книжная полка %(name)s удалена" msgstr "Книжная полка %(name)s удалена"
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "Книжная полка: '%(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" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "" msgstr ""
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "Изменить расположение книжной полки '%(name)s'" msgstr "Изменить расположение книжной полки '%(name)s'"
#: cps/web.py:2326 #: cps/web.py:2306
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "Найдена учётная запись для для данного адреса email." msgstr "Найдена учётная запись для для данного адреса email."
#: cps/web.py:2328 cps/web.py:2332 #: cps/web.py:2308 cps/web.py:2312
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "Профиль %(name)s" msgstr "Профиль %(name)s"
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "Профиль обновлён" msgstr "Профиль обновлён"
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "Администрирование" msgstr "Администрирование"
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Конфигурация Calibre-web обновлена" 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" msgid "Basic Configuration"
msgstr "Настройки сервера" msgstr "Настройки сервера"
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "Неверный путь к фалу БД, пожалуйста, укажите правильное расположение БД" 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" msgid "Add new user"
msgstr "Добавить пользователя" msgstr "Добавить пользователя"
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "Пользователь '%(user)s' добавлен" msgstr "Пользователь '%(user)s' добавлен"
#: cps/web.py:2564 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "Для указанного адреса или имени найдена существующая учётная запись." msgstr "Для указанного адреса или имени найдена существующая учётная запись."
#: cps/web.py:2586 #: cps/web.py:2566
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "Настройки почты изменены" msgstr "Настройки почты изменены"
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "Тестовое сообщение успешно отправлено на адрес %(kindlemail)s" msgstr "Тестовое сообщение успешно отправлено на адрес %(kindlemail)s"
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "Ошибка отправки тестового сообщения: %(res)s" msgstr "Ошибка отправки тестового сообщения: %(res)s"
#: cps/web.py:2600 #: cps/web.py:2580
msgid "E-Mail settings updated" msgid "E-Mail settings updated"
msgstr "Обновлены настройки e-mail" msgstr "Обновлены настройки e-mail"
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "Изменить почтовые настройки" msgstr "Изменить почтовые настройки"
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "Пользователь '%(nick)s' удалён" msgstr "Пользователь '%(nick)s' удалён"
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "Пользователь '%(nick)s' обновлён" msgstr "Пользователь '%(nick)s' обновлён"
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "Произошла неизвестная ошибка." msgstr "Произошла неизвестная ошибка."
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "Изменить пользователя %(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" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "" 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" msgid "edit metadata"
msgstr "изменить метаданные" msgstr "изменить метаданные"
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "неизвестно" msgstr "неизвестно"
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" msgid "File extension \"%s\" is not allowed to be uploaded to this server"
msgstr "Запрещена загрузка файлов с расширением \"%s\"" msgstr "Запрещена загрузка файлов с расширением \"%s\""
#: cps/web.py:2978 #: cps/web.py:2960
msgid "File to be uploaded must have an extension" msgid "File to be uploaded must have an extension"
msgstr "Загружаемый файл должен иметь расширение" msgstr "Загружаемый файл должен иметь расширение"
#: cps/web.py:2997 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "Ошибка при создании пути %s (доступ запрещён)" msgstr "Ошибка при создании пути %s (доступ запрещён)"
#: cps/web.py:3002 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "Ошибка записи файоа %s (доступ запрещён)" msgstr "Ошибка записи файоа %s (доступ запрещён)"
#: cps/web.py:3007 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "Ошибка удаления файла %s (доступ запрещён)" msgstr "Ошибка удаления файла %s (доступ запрещён)"
@ -607,6 +607,18 @@ msgstr "Вы действительно хотите остановить Calibr
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "Установка обновлений, пожалуйста, не обновляйте страницу." 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "Удалить книгу" msgstr "Удалить книгу"
@ -615,12 +627,13 @@ msgstr "Удалить книгу"
msgid "Book Title" msgid "Book Title"
msgstr "Название" msgstr "Название"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "Автор" 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" msgid "Description"
msgstr "Описание" msgstr "Описание"
@ -703,35 +716,35 @@ msgstr " Поиск по ключевому слову"
msgid "Go!" msgid "Go!"
msgstr "Искать" msgstr "Искать"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "Нажмите на обложку, чтобы получить метаданные" 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..." msgid "Loading..."
msgstr "Загрузка..." 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" msgid "Close"
msgstr "Закрыть" msgstr "Закрыть"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "Издатель" msgstr "Издатель"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "Источник" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Расположение БД Calibre" msgstr "Расположение БД Calibre"
@ -821,7 +834,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "Настройки по умолчанию для новых пользователей" 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" msgid "Admin user"
msgstr "Управление сервером" msgstr "Управление сервером"
@ -922,6 +935,11 @@ msgstr "Сохранить настройки и отправить тестов
msgid "Next" msgid "Next"
msgstr "Дальше" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "Обзор (случайные книги)" msgstr "Обзор (случайные книги)"
@ -930,52 +948,47 @@ msgstr "Обзор (случайные книги)"
msgid "Start" msgid "Start"
msgstr "Старт" msgstr "Старт"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "Поиск"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "Популярные книги" msgstr "Популярные книги"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "Популярные книги в этом каталоге, на основе количества скачиваний" 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" msgid "Best rated Books"
msgstr "Книги с наилучшим рейтингом" msgstr "Книги с наилучшим рейтингом"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "Популярные книги из этого каталога на основании рейтинга" msgstr "Популярные книги из этого каталога на основании рейтинга"
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "Новые" msgstr "Новые"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "Последние поступления" msgstr "Последние поступления"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "Показывать случайные книги" msgstr "Показывать случайные книги"
#: cps/templates/index.xml:57 cps/templates/layout.html:139 #: cps/templates/index.xml:56 cps/templates/layout.html:139
msgid "Authors" msgid "Authors"
msgstr "Авторы" msgstr "Авторы"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "Книги, отсортированные по автору" msgstr "Книги, отсортированные по автору"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "Книги, отсортированные по категории" msgstr "Книги, отсортированные по категории"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "Книги, отсортированные по серии" msgstr "Книги, отсортированные по серии"
@ -1086,7 +1099,11 @@ msgstr ""
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "Каталог электронных книг Calibre Web" 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." msgid "Reflow text when sidebars are open."
msgstr "Обновить размещение текста при открытии боковой панели" msgstr "Обновить размещение текста при открытии боковой панели"
@ -1135,8 +1152,8 @@ msgid "No Results for:"
msgstr "Ничего не найдено по запросу:" msgstr "Ничего не найдено по запросу:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "Попробуйте изменить критерии поиска" msgstr "Попробуйте изменить критерии поиск"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
msgid "Results for:" msgid "Results for:"
@ -1227,45 +1244,45 @@ msgid "Show all"
msgstr "Всех" msgstr "Всех"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "Показывать случайные книги" msgstr "Показывать случайные книги"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "Показывать популярные книги" msgstr "Показывать популярные книги"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "Показывать книги с наивысшим рейтингом" msgstr "Показывать книги с наивысшим рейтингом"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "Показывать выбор языка" msgstr "Показывать выбор языка"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "Показывать выбор серии" msgstr "Показывать выбор серии"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "Показывать выбор категории" msgstr "Показывать выбор категории"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "Показывать выбор автора" msgstr "Показывать выбор автора"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "Показывать прочитанные и непрочитанные" msgstr "Показывать прочитанные и непрочитанные"
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:78
msgid "Show random books in detail view" msgid "Show random books in detail view"
msgstr "Показывать случайные книги при просмотре деталей" msgstr "Показывать случайные книги при просмотре деталей"
#: cps/templates/user_edit.html:90
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:123 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "Удалить этого пользователя" msgstr "Удалить этого пользователя"

@ -15,7 +15,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Calibre-web\n" "Project-Id-Version: Calibre-web\n"
"Report-Msgid-Bugs-To: https://github.com/janeczku/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" "PO-Revision-Date: 2017-01-06 17:00+0000\n"
"Last-Translator: dalin <dalin.lin@gmail.com>\n" "Last-Translator: dalin <dalin.lin@gmail.com>\n"
"Language: zh_Hans_CN\n" "Language: zh_Hans_CN\n"
@ -26,7 +26,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "未安装" msgstr "未安装"
@ -70,373 +70,373 @@ msgstr "发送到Kindle"
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "无法找到适合邮件发送的格式" msgstr "无法找到适合邮件发送的格式"
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "游客" msgstr "游客"
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "正在请求更新包" msgstr "正在请求更新包"
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "正在下载更新包" msgstr "正在下载更新包"
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "正在解压更新包" msgstr "正在解压更新包"
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "文件已替换" msgstr "文件已替换"
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "数据库连接已关闭" msgstr "数据库连接已关闭"
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "服务器已停止" msgstr "服务器已停止"
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "更新完成,请按确定并刷新页面" msgstr "更新完成,请按确定并刷新页面"
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "最近添加的书籍" msgstr "最近添加的书籍"
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "最新书籍" msgstr "最新书籍"
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "最旧书籍" msgstr "最旧书籍"
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "书籍 (A-Z)" msgstr "书籍 (A-Z)"
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "书籍 (Z-A)" msgstr "书籍 (Z-A)"
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "热门书籍(最多下载)" msgstr "热门书籍(最多下载)"
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "最高评分书籍" msgstr "最高评分书籍"
#: cps/templates/index.xml:36 cps/web.py:1145 #: cps/templates/index.xml:35 cps/web.py:1115
msgid "Random Books" msgid "Random Books"
msgstr "随机书籍" msgstr "随机书籍"
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "作者列表" 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:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "无法打开电子书。 文件不存在或者文件不可访问:" msgstr "无法打开电子书。 文件不存在或者文件不可访问:"
#: cps/templates/index.xml:71 cps/web.py:1198 #: cps/templates/index.xml:70 cps/web.py:1176
msgid "Series list" msgid "Series list"
msgstr "丛书列表" msgstr "丛书列表"
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "丛书: %(serie)s" msgstr "丛书: %(serie)s"
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "可用语言" msgstr "可用语言"
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "语言: %(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" msgid "Category list"
msgstr "分类列表" msgstr "分类列表"
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "分类: %(name)s" msgstr "分类: %(name)s"
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "可执行权限缺失" msgstr "可执行权限缺失"
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "统计" msgstr "统计"
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "服务器已重启,请刷新页面" msgstr "服务器已重启,请刷新页面"
#: cps/web.py:1565 #: cps/web.py:1549
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
msgstr "正在关闭服务器,请关闭窗口" msgstr "正在关闭服务器,请关闭窗口"
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "更新完成" msgstr "更新完成"
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "搜索" msgstr "搜索"
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "已读书籍" msgstr "已读书籍"
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "未读书籍" 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" msgid "Read a Book"
msgstr "阅读一本书" msgstr "阅读一本书"
#: cps/web.py:1888 cps/web.py:2513 #: cps/web.py:1868 cps/web.py:2493
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "请填写所有字段" 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" msgid "register"
msgstr "注册" msgstr "注册"
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "发生一个未知错误。请稍后再试。" msgstr "发生一个未知错误。请稍后再试。"
#: cps/web.py:1909 #: cps/web.py:1889
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "此用户名或邮箱已被使用。" msgstr "此用户名或邮箱已被使用。"
#: cps/web.py:1928 cps/web.py:2024 #: cps/web.py:1908 cps/web.py:2004
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "您现在已以'%(nickname)s'身份登录" msgstr "您现在已以'%(nickname)s'身份登录"
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "用户名或密码错误" msgstr "用户名或密码错误"
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "登录" msgstr "登录"
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "找不到Token" msgstr "找不到Token"
#: cps/web.py:1980 cps/web.py:2011 #: cps/web.py:1960 cps/web.py:1991
msgid "Token has expired" msgid "Token has expired"
msgstr "Token已过期" msgstr "Token已过期"
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "成功!请返回您的设备" msgstr "成功!请返回您的设备"
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "请先配置SMTP邮箱..." msgstr "请先配置SMTP邮箱..."
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "此书已被成功发给 %(kindlemail)s" msgstr "此书已被成功发给 %(kindlemail)s"
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "发送这本书的时候出现错误: %(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..." msgid "Please configure your kindle email address first..."
msgstr "请先配置您的kindle电子邮箱地址..." msgstr "请先配置您的kindle电子邮箱地址..."
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "此书已被添加到书架: %(sname)s" msgstr "此书已被添加到书架: %(sname)s"
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "此书已从书架 %(sname)s 中删除" msgstr "此书已从书架 %(sname)s 中删除"
#: cps/web.py:2146 cps/web.py:2170 #: cps/web.py:2126 cps/web.py:2150
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "已存在书架 '%(title)s'。" msgstr "已存在书架 '%(title)s'。"
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "书架 %(title)s 已被创建" msgstr "书架 %(title)s 已被创建"
#: cps/web.py:2153 cps/web.py:2181 #: cps/web.py:2133 cps/web.py:2161
msgid "There was an error" msgid "There was an error"
msgstr "发生错误" msgstr "发生错误"
#: cps/web.py:2154 cps/web.py:2156 #: cps/web.py:2134 cps/web.py:2136
msgid "create a shelf" msgid "create a shelf"
msgstr "创建书架" msgstr "创建书架"
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "书架 %(title)s 已被修改" msgstr "书架 %(title)s 已被修改"
#: cps/web.py:2182 cps/web.py:2184 #: cps/web.py:2162 cps/web.py:2164
msgid "Edit a shelf" msgid "Edit a shelf"
msgstr "编辑书架" msgstr "编辑书架"
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "成功删除书架 %(name)s" msgstr "成功删除书架 %(name)s"
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "书架: '%(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" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "打开书架出错。书架不存在或不可访问" msgstr "打开书架出错。书架不存在或不可访问"
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "修改书架 '%(name)s' 顺序" msgstr "修改书架 '%(name)s' 顺序"
#: cps/web.py:2326 #: cps/web.py:2306
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "找到已使用此邮箱的账号。" msgstr "找到已使用此邮箱的账号。"
#: cps/web.py:2328 cps/web.py:2332 #: cps/web.py:2308 cps/web.py:2312
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "%(name)s 的资料" msgstr "%(name)s 的资料"
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "资料已更新" msgstr "资料已更新"
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "管理页" msgstr "管理页"
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "Calibre-web配置已更新" 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" msgid "Basic Configuration"
msgstr "基本配置" msgstr "基本配置"
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "DB位置无效请输入正确路径" 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" msgid "Add new user"
msgstr "添加新用户" msgstr "添加新用户"
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "用户 '%(user)s' 已被创建" msgstr "用户 '%(user)s' 已被创建"
#: cps/web.py:2564 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "已存在使用此邮箱或昵称的账号。" msgstr "已存在使用此邮箱或昵称的账号。"
#: cps/web.py:2586 #: cps/web.py:2566
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "邮箱设置已更新" msgstr "邮箱设置已更新"
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "测试邮件已成功发送到 %(kindlemail)s" msgstr "测试邮件已成功发送到 %(kindlemail)s"
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "发送测试邮件时发生错误: %(res)s" msgstr "发送测试邮件时发生错误: %(res)s"
#: cps/web.py:2600 #: cps/web.py:2580
msgid "E-Mail settings updated" msgid "E-Mail settings updated"
msgstr "E-Mail 设置已更新" msgstr "E-Mail 设置已更新"
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "编辑邮箱设置" msgstr "编辑邮箱设置"
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "用户 '%(nick)s' 已被删除" msgstr "用户 '%(nick)s' 已被删除"
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "用户 '%(nick)s' 已被更新" msgstr "用户 '%(nick)s' 已被更新"
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "发生未知错误。" msgstr "发生未知错误。"
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "编辑用户 %(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" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "打开电子书出错。文件不存在或不可访问" 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" msgid "edit metadata"
msgstr "编辑元数据" msgstr "编辑元数据"
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "未知" msgstr "未知"
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" msgid "File extension \"%s\" is not allowed to be uploaded to this server"
msgstr "不能上传后缀为 \"%s\" 的文件到此服务器" msgstr "不能上传后缀为 \"%s\" 的文件到此服务器"
#: cps/web.py:2978 #: cps/web.py:2960
msgid "File to be uploaded must have an extension" msgid "File to be uploaded must have an extension"
msgstr "要上传的文件必须有一个后缀" msgstr "要上传的文件必须有一个后缀"
#: cps/web.py:2997 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "创建路径 %s 失败(权限拒绝)。" msgstr "创建路径 %s 失败(权限拒绝)。"
#: cps/web.py:3002 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "存储文件 %s 失败(权限拒绝)。" msgstr "存储文件 %s 失败(权限拒绝)。"
#: cps/web.py:3007 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "删除文件 %s 失败(权限拒绝)。" msgstr "删除文件 %s 失败(权限拒绝)。"
@ -607,6 +607,18 @@ msgstr "您确定要关闭 Calibre-web 吗?"
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "正在更新,请不要刷新页面" 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "删除书籍" msgstr "删除书籍"
@ -615,12 +627,13 @@ msgstr "删除书籍"
msgid "Book Title" msgid "Book Title"
msgstr "书名" msgstr "书名"
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "作者" 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" msgid "Description"
msgstr "简介" msgstr "简介"
@ -703,35 +716,35 @@ msgstr "搜索关键字"
msgid "Go!" msgid "Go!"
msgstr "走起!" msgstr "走起!"
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "点击封面加载元数据到表单" 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..." msgid "Loading..."
msgstr "加载中..." 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" msgid "Close"
msgstr "关闭" msgstr "关闭"
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
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
msgid "Publisher" msgid "Publisher"
msgstr "出版社" msgstr "出版社"
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "Source" msgid "Source"
msgstr "来源" 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 #: cps/templates/config_edit.html:7
msgid "Location of Calibre database" msgid "Location of Calibre database"
msgstr "Calibre 数据库位置" msgstr "Calibre 数据库位置"
@ -821,7 +834,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "新用户默认设置" 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" msgid "Admin user"
msgstr "管理用户" msgstr "管理用户"
@ -922,6 +935,11 @@ msgstr "保存设置并发送测试邮件"
msgid "Next" msgid "Next"
msgstr "下一个" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "发现(随机书籍)" msgstr "发现(随机书籍)"
@ -930,52 +948,47 @@ msgstr "发现(随机书籍)"
msgid "Start" msgid "Start"
msgstr "开始" msgstr "开始"
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr "搜索"
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "热门书籍" msgstr "热门书籍"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "基于下载数的热门书籍" 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" msgid "Best rated Books"
msgstr "最高评分书籍" msgstr "最高评分书籍"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "基于评分的热门书籍" msgstr "基于评分的热门书籍"
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "新书" msgstr "新书"
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "最新书籍" msgstr "最新书籍"
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "显示随机书籍" msgstr "显示随机书籍"
#: cps/templates/index.xml:57 cps/templates/layout.html:139 #: cps/templates/index.xml:56 cps/templates/layout.html:139
msgid "Authors" msgid "Authors"
msgstr "作者" msgstr "作者"
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "书籍按作者排序" msgstr "书籍按作者排序"
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "书籍按分类排序" msgstr "书籍按分类排序"
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "书籍按丛书排序" msgstr "书籍按丛书排序"
@ -1086,7 +1099,11 @@ msgstr "通过魔法链接登录"
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "" 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." msgid "Reflow text when sidebars are open."
msgstr "" msgstr ""
@ -1135,7 +1152,7 @@ msgid "No Results for:"
msgstr "找不到结果:" msgstr "找不到结果:"
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "请尝试别的关键字" msgstr "请尝试别的关键字"
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1227,45 +1244,45 @@ msgid "Show all"
msgstr "显示全部" msgstr "显示全部"
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show random books"
msgstr "显示随机书籍" msgstr "显示随机书籍"
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:50
msgid "Show hot books" msgid "Show hot books"
msgstr "显示热门书籍" msgstr "显示热门书籍"
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:54
msgid "Show best rated books" msgid "Show best rated books"
msgstr "显示最高评分书籍" msgstr "显示最高评分书籍"
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:58
msgid "Show language selection" msgid "Show language selection"
msgstr "显示语言选择" msgstr "显示语言选择"
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:62
msgid "Show series selection" msgid "Show series selection"
msgstr "显示丛书选择" msgstr "显示丛书选择"
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:66
msgid "Show category selection" msgid "Show category selection"
msgstr "显示分类选择" msgstr "显示分类选择"
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:70
msgid "Show author selection" msgid "Show author selection"
msgstr "显示作者选择" msgstr "显示作者选择"
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:74
msgid "Show read and unread" msgid "Show read and unread"
msgstr "显示已读和未读" msgstr "显示已读和未读"
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:78
msgid "Show random books in detail view" msgid "Show random books in detail view"
msgstr "在详情页显示随机书籍" msgstr "在详情页显示随机书籍"
#: cps/templates/user_edit.html:90
msgid "Show mature content"
msgstr ""
#: cps/templates/user_edit.html:123 #: cps/templates/user_edit.html:123
msgid "Delete this user" msgid "Delete this user"
msgstr "删除此用户" msgstr "删除此用户"

@ -6,6 +6,7 @@ from sqlalchemy import exc
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import * from sqlalchemy.orm import *
from flask_login import AnonymousUserMixin from flask_login import AnonymousUserMixin
import sys
import os import os
import logging import logging
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
@ -50,7 +51,7 @@ DEVELOPMENT = False
class UserBase: class UserBase:
@staticmethod @classmethod
def is_authenticated(self): def is_authenticated(self):
return True return True
@ -171,6 +172,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
settings = session.query(Settings).first() settings = session.query(Settings).first()
self.nickname = data.nickname self.nickname = data.nickname
self.role = data.role self.role = data.role
self.id=data.id
self.sidebar_view = data.sidebar_view self.sidebar_view = data.sidebar_view
self.default_language = data.default_language self.default_language = data.default_language
self.locale = data.locale self.locale = data.locale
@ -186,6 +188,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
def is_anonymous(self): def is_anonymous(self):
return self.anon_browse return self.anon_browse
def is_authenticated(self):
return False
# Baseclass representing Shelfs in calibre-web inapp.db # Baseclass representing Shelfs in calibre-web inapp.db
class Shelf(Base): class Shelf(Base):
@ -212,15 +216,26 @@ class BookShelf(Base):
def __repr__(self): def __repr__(self):
return '<Book %r>' % self.id return '<Book %r>' % self.id
class ReadBook(Base): class ReadBook(Base):
__tablename__ = 'book_read_link' __tablename__ = 'book_read_link'
id=Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
book_id = Column(Integer, unique=False) 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) 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 # Baseclass representing Downloads from calibre-web in app.db
class Downloads(Base): class Downloads(Base):
__tablename__ = 'downloads' __tablename__ = 'downloads'
@ -376,7 +391,11 @@ class Config:
(self.config_default_role & ROLE_DELETE_BOOKS == ROLE_DELETE_BOOKS)) (self.config_default_role & ROLE_DELETE_BOOKS == ROLE_DELETE_BOOKS))
def mature_content_tags(self): 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): def get_Log_Level(self):
ret_value="" ret_value=""
@ -396,7 +415,9 @@ class Config:
# rows with SQL commands # rows with SQL commands
def migrate_Database(): def migrate_Database():
if not engine.dialect.has_table(engine.connect(), "book_read_link"): 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: try:
session.query(exists().where(User.locale)).scalar() session.query(exists().where(User.locale)).scalar()

@ -13,6 +13,12 @@ try:
except ImportError: except ImportError:
goodreads_support = False goodreads_support = False
try:
import Levenshtein
levenshtein_support = True
except ImportError:
levenshtein_support = False
try: try:
from functools import reduce from functools import reduce
except ImportError: except ImportError:
@ -73,6 +79,7 @@ import hashlib
from redirect import redirect_back, is_safe_url from redirect import redirect_back, is_safe_url
from tornado import version as tornadoVersion from tornado import version as tornadoVersion
from socket import error as SocketError
try: try:
from urllib.parse import quote from urllib.parse import quote
@ -151,7 +158,7 @@ class Gauth:
@Singleton @Singleton
class Gdrive: class Gdrive:
def __init__(self): def __init__(self):
self.drive = gdriveutils.getDrive(Gauth.Instance().auth) self.drive = gdriveutils.getDrive(gauth=Gauth.Instance().auth)
class ReverseProxied(object): class ReverseProxied(object):
@ -196,6 +203,7 @@ class ReverseProxied(object):
mimetypes.init() mimetypes.init()
mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('application/xhtml+xml', '.xhtml')
mimetypes.add_type('application/epub+zip', '.epub') 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', '.mobi')
mimetypes.add_type('application/x-mobipocket-ebook', '.prc') mimetypes.add_type('application/x-mobipocket-ebook', '.prc')
mimetypes.add_type('application/vnd.amazon.ebook', '.azw') mimetypes.add_type('application/vnd.amazon.ebook', '.azw')
@ -511,7 +519,7 @@ def common_filters():
if current_user.filter_language() != "all": if current_user.filter_language() != "all":
lang_filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language()) lang_filter = db.Books.languages.any(db.Languages.lang_code == current_user.filter_language())
else: else:
lang_filter = True lang_filter = true()
content_rating_filter = false() if current_user.mature_content else \ content_rating_filter = false() if current_user.mature_content else \
db.Books.tags.any(db.Tags.name.in_(config.mature_content_tags())) db.Books.tags.any(db.Tags.name.in_(config.mature_content_tags()))
return and_(lang_filter, ~content_rating_filter) 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, pagination = Pagination(page, config.config_books_per_page,
len(db.session.query(database) len(db.session.query(database)
.filter(db_filter).filter(common_filters()).all())) .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) .order_by(order).offset(off).limit(config.config_books_per_page)
return entries, random, pagination return entries, random, pagination
@ -624,7 +632,7 @@ def before_request():
def feed_index(): def feed_index():
xml = render_title_template('index.xml') xml = render_title_template('index.xml')
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -633,7 +641,7 @@ def feed_index():
def feed_osd(): def feed_osd():
xml = render_title_template('osd.xml', lang='de-DE') xml = render_title_template('osd.xml', lang='de-DE')
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/xml; charset=utf-8"
return response return response
@ -663,7 +671,7 @@ def feed_search(term):
else: else:
xml = render_title_template('feed.xml', searchterm="") xml = render_title_template('feed.xml', searchterm="")
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -677,7 +685,7 @@ def feed_new():
db.Books, True, db.Books.timestamp.desc()) db.Books, True, db.Books.timestamp.desc())
xml = render_title_template('feed.xml', entries=entries, pagination=pagination) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -689,7 +697,7 @@ def feed_discover():
pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page)) pagination = Pagination(1, config.config_books_per_page, int(config.config_books_per_page))
xml = render_title_template('feed.xml', entries=entries, pagination=pagination) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -703,7 +711,7 @@ def feed_best_rated():
db.Books, db.Books.ratings.any(db.Ratings.rating > 9), db.Books.timestamp.desc()) db.Books, db.Books.ratings.any(db.Ratings.rating > 9), db.Books.timestamp.desc())
xml = render_title_template('feed.xml', entries=entries, pagination=pagination) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response 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) 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) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -747,7 +755,7 @@ def feed_authorindex():
len(db.session.query(db.Authors).all())) len(db.session.query(db.Authors).all()))
xml = render_title_template('feed.xml', listelements=entries, folder='feed_author', pagination=pagination) xml = render_title_template('feed.xml', listelements=entries, folder='feed_author', pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response 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()) 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) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -777,7 +785,7 @@ def feed_categoryindex():
len(db.session.query(db.Tags).all())) len(db.session.query(db.Tags).all()))
xml = render_title_template('feed.xml', listelements=entries, folder='feed_category', pagination=pagination) xml = render_title_template('feed.xml', listelements=entries, folder='feed_category', pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -787,11 +795,11 @@ def feed_category(book_id):
off = request.args.get("offset") off = request.args.get("offset")
if not off: if not off:
off = 0 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()) 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) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response return response
@ -807,7 +815,7 @@ def feed_seriesindex():
len(db.session.query(db.Series).all())) len(db.session.query(db.Series).all()))
xml = render_title_template('feed.xml', listelements=entries, folder='feed_series', pagination=pagination) xml = render_title_template('feed.xml', listelements=entries, folder='feed_series', pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response 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) 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) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
response.headers["Content-Type"] = "application/xml" response.headers["Content-Type"] = "application/atom+xml; charset=utf-8"
return response 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() 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() 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) app.logger.info(data.name)
if current_user.is_authenticated: if current_user.is_authenticated():
helper.update_download(book_id, int(current_user.id)) helper.update_download(book_id, int(current_user.id))
file_name = book.title file_name = book.title
if len(book.authors) > 0: 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) file_name = helper.get_valid_filename(file_name)
headers = Headers() headers = Headers()
headers["Content-Disposition"] = "attachment; filename*=UTF-8''%s.%s" % (quote(file_name.encode('utf8')), book_format) 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() startTime = time.time()
if config.config_use_google_drive: if config.config_use_google_drive:
app.logger.info(time.time() - startTime) 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) hot_books = all_books.offset(off).limit(config.config_books_per_page)
entries = list() entries = list()
for book in hot_books: 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: if downloadBook:
entries.append( entries.append(downloadBook)
db.session.query(db.Books).filter(common_filters())
.filter(db.Books.id == book.Downloads.book_id).first()
)
else: else:
ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete() ub.session.query(ub.Downloads).filter(book.Downloads.book_id == ub.Downloads.book_id).delete()
ub.session.commit() ub.session.commit()
@ -1118,6 +1127,8 @@ def author_list():
entries = db.session.query(db.Authors, func.count('books_authors_link.book').label('count'))\ 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())\ .join(db.books_authors_link).join(db.Books).filter(common_filters())\
.group_by('books_authors_link.author').order_by(db.Authors.sort).all() .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")) 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>'") @app.route("/author/<int:book_id>/<int:page>'")
@login_required_if_no_ano @login_required_if_no_ano
def author(book_id, page): 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()) db.Books.timestamp.desc())
if entries is None: if entries is None:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect(url_for("index")) 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 author_info = None
other_books = None other_books = []
if goodreads_support and config.config_use_goodreads: if goodreads_support and config.config_use_goodreads:
gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret) gc = GoodreadsClient(config.config_goodreads_api_key, config.config_goodreads_api_secret)
author_info = gc.find_author(author_name=name) author_info = gc.find_author(author_name=name)
other_books = get_unique_other_books(entries.all(), author_info.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), entries.all(), [])
other_books = filter(lambda book: book.isbn not in identifiers and book.gid["#text"] not in identifiers, author_info.books)
return render_title_template('author.html', entries=entries, pagination=pagination, return render_title_template('author.html', entries=entries, pagination=pagination,
title=name, author=author_info, other_books=other_books) 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") @app.route("/series")
@login_required_if_no_ano @login_required_if_no_ano
def series_list(): def series_list():
@ -1186,13 +1212,12 @@ def language_overview():
lang.name = _(isoLanguages.get(part3=lang.lang_code).name) lang.name = _(isoLanguages.get(part3=lang.lang_code).name)
else: else:
try: try:
langfound = 1
cur_l = LC.parse(current_user.filter_language()) cur_l = LC.parse(current_user.filter_language())
except Exception: except Exception:
langfound = 0 cur_l = None
languages = db.session.query(db.Languages).filter( languages = db.session.query(db.Languages).filter(
db.Languages.lang_code == current_user.filter_language()).all() db.Languages.lang_code == current_user.filter_language()).all()
if langfound: if cur_l:
languages[0].name = cur_l.get_language_name(get_locale()) languages[0].name = cur_l.get_language_name(get_locale())
else: else:
languages[0].name = _(isoLanguages.get(part3=languages[0].lang_code).name) 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")) 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") @app.route("/admin")
@login_required @login_required
def admin_forbidden(): def admin_forbidden():
@ -1672,15 +1717,21 @@ def feed_get_cover(book_id):
def render_read_books(page, are_read, as_xml=False): 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() if not current_user.is_anonymous():
readBookIds = [x.book_id for x in readBooks] readBooks = ub.session.query(ub.ReadBook).filter(ub.ReadBook.user_id == int(current_user.id)).filter(ub.ReadBook.is_read == True).all()
if are_read: readBookIds = [x.book_id for x in readBooks]
db_filter = db.Books.id.in_(readBookIds) 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: else:
db_filter = ~db.Books.id.in_(readBookIds) 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: if as_xml:
xml = render_title_template('feed.xml', entries=entries, pagination=pagination) xml = render_title_template('feed.xml', entries=entries, pagination=pagination)
response = make_response(xml) response = make_response(xml)
@ -1732,49 +1783,55 @@ def unread_books(page):
@login_required_if_no_ano @login_required_if_no_ano
def read_book(book_id, book_format): def read_book(book_id, book_format):
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
if book: if not 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:
flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error") flash(_(u"Error opening eBook. File does not exist or file is not accessible:"), category="error")
return redirect(url_for("index")) 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>") @app.route("/download/<int:book_id>/<book_format>")
@login_required_if_no_ano @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() data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == book_format.upper()).first()
if data: if data:
# collect downloaded books only for registered user and not for anonymous user # 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)) helper.update_download(book_id, int(current_user.id))
file_name = book.title file_name = book.title
if len(book.authors) > 0: if len(book.authors) > 0:
@ -1819,7 +1876,7 @@ def get_download_link_ext(book_id, book_format, anyname):
def register(): def register():
if not config.config_public_reg: if not config.config_public_reg:
abort(404) 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')) return redirect(url_for('index'))
if request.method == "POST": if request.method == "POST":
@ -1856,7 +1913,7 @@ def register():
def login(): def login():
if not config.db_configured: if not config.db_configured:
return redirect(url_for('basic_configuration')) 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')) return redirect(url_for('index'))
if request.method == "POST": if request.method == "POST":
form = request.form.to_dict() form = request.form.to_dict()
@ -1883,7 +1940,7 @@ def login():
@app.route('/logout') @app.route('/logout')
@login_required @login_required
def logout(): 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() logout_user()
return redirect(url_for('login')) return redirect(url_for('login'))
@ -2128,10 +2185,11 @@ def edit_shelf(shelf_id):
@login_required @login_required
def delete_shelf(shelf_id): def delete_shelf(shelf_id):
cur_shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first() cur_shelf = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).first()
deleted = false
if current_user.role_admin(): if current_user.role_admin():
deleted = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).delete() deleted = ub.session.query(ub.Shelf).filter(ub.Shelf.id == shelf_id).delete()
else: 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()): 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), deleted = ub.session.query(ub.Shelf).filter(ub.or_(ub.and_(ub.Shelf.user_id == int(current_user.id),
ub.Shelf.id == shelf_id), ub.Shelf.id == shelf_id),
@ -2697,7 +2755,7 @@ def edit_book(book_id):
except Exception: except Exception:
book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name) book.languages[index].language_name = _(isoLanguages.get(part3=book.languages[index].lang_code).name)
for author in book.authors: for author in book.authors:
author_names.append(author.name) author_names.append(author.name.replace('|',','))
# Show form # Show form
if request.method != 'POST': if request.method != 'POST':
@ -2706,12 +2764,41 @@ def edit_book(book_id):
# Update book # Update book
edited_books_id = set() 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() to_save = request.form.to_dict()
if book.title != to_save["book_title"]: if book.title != to_save["book_title"]:
book.title = to_save["book_title"] book.title = to_save["book_title"]
edited_books_id.add(book.id) edited_books_id.add(book.id)
input_authors = to_save["author_name"].split('&') 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 # we have all author names now
if input_authors == ['']: if input_authors == ['']:
input_authors = [_(u'unknown')] # prevent empty Author input_authors = [_(u'unknown')] # prevent empty Author
@ -3028,5 +3115,11 @@ def upload():
def start_gevent(): def start_gevent():
from gevent.wsgi import WSGIServer from gevent.wsgi import WSGIServer
global gevent_server global gevent_server
gevent_server = WSGIServer(('', ub.config.config_port), app) try:
gevent_server.serve_forever() 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()

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PROJECT VERSION\n" "Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.4.0\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" msgid "not installed"
msgstr "" msgstr ""
@ -61,373 +61,373 @@ msgstr ""
msgid "Could not find any formats suitable for sending by email" msgid "Could not find any formats suitable for sending by email"
msgstr "" msgstr ""
#: cps/ub.py:542 #: cps/ub.py:556
msgid "Guest" msgid "Guest"
msgstr "" msgstr ""
#: cps/web.py:974 #: cps/web.py:953
msgid "Requesting update package" msgid "Requesting update package"
msgstr "" msgstr ""
#: cps/web.py:975 #: cps/web.py:954
msgid "Downloading update package" msgid "Downloading update package"
msgstr "" msgstr ""
#: cps/web.py:976 #: cps/web.py:955
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "" msgstr ""
#: cps/web.py:977 #: cps/web.py:956
msgid "Files are replaced" msgid "Files are replaced"
msgstr "" msgstr ""
#: cps/web.py:978 #: cps/web.py:957
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "" msgstr ""
#: cps/web.py:979 #: cps/web.py:958
msgid "Server is stopped" msgid "Server is stopped"
msgstr "" msgstr ""
#: cps/web.py:980 #: cps/web.py:959
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "" msgstr ""
#: cps/web.py:1054 #: cps/web.py:1033
msgid "Recently Added Books" msgid "Recently Added Books"
msgstr "" msgstr ""
#: cps/web.py:1063 #: cps/web.py:1042
msgid "Newest Books" msgid "Newest Books"
msgstr "" msgstr ""
#: cps/web.py:1072 #: cps/web.py:1051
msgid "Oldest Books" msgid "Oldest Books"
msgstr "" msgstr ""
#: cps/web.py:1081 #: cps/web.py:1060
msgid "Books (A-Z)" msgid "Books (A-Z)"
msgstr "" msgstr ""
#: cps/web.py:1090 #: cps/web.py:1069
msgid "Books (Z-A)" msgid "Books (Z-A)"
msgstr "" msgstr ""
#: cps/web.py:1126 #: cps/web.py:1096
msgid "Hot Books (most downloaded)" msgid "Hot Books (most downloaded)"
msgstr "" msgstr ""
#: cps/web.py:1136 #: cps/web.py:1106
msgid "Best rated books" msgid "Best rated books"
msgstr "" msgstr ""
#: cps/templates/index.xml:36 cps/web.py:1145 #: cps/templates/index.xml:35 cps/web.py:1115
msgid "Random Books" msgid "Random Books"
msgstr "" msgstr ""
#: cps/web.py:1161 #: cps/web.py:1124
msgid "Author list" msgid "Author list"
msgstr "" 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:" msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "" msgstr ""
#: cps/templates/index.xml:71 cps/web.py:1198 #: cps/templates/index.xml:70 cps/web.py:1176
msgid "Series list" msgid "Series list"
msgstr "" msgstr ""
#: cps/web.py:1210 #: cps/web.py:1188
#, python-format #, python-format
msgid "Series: %(serie)s" msgid "Series: %(serie)s"
msgstr "" msgstr ""
#: cps/web.py:1243 #: cps/web.py:1221
msgid "Available languages" msgid "Available languages"
msgstr "" msgstr ""
#: cps/web.py:1258 #: cps/web.py:1236
#, python-format #, python-format
msgid "Language: %(name)s" msgid "Language: %(name)s"
msgstr "" msgstr ""
#: cps/templates/index.xml:64 cps/web.py:1274 #: cps/templates/index.xml:63 cps/web.py:1245
msgid "Category list" msgid "Category list"
msgstr "" msgstr ""
#: cps/web.py:1286 #: cps/web.py:1257
#, python-format #, python-format
msgid "Category: %(name)s" msgid "Category: %(name)s"
msgstr "" msgstr ""
#: cps/web.py:1385 #: cps/web.py:1369
msgid "Excecution permissions missing" msgid "Excecution permissions missing"
msgstr "" msgstr ""
#: cps/web.py:1399 #: cps/web.py:1383
msgid "Statistics" msgid "Statistics"
msgstr "" msgstr ""
#: cps/web.py:1563 #: cps/web.py:1547
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "" msgstr ""
#: cps/web.py:1565 #: cps/web.py:1549
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
msgstr "" msgstr ""
#: cps/web.py:1581 #: cps/web.py:1565
msgid "Update done" msgid "Update done"
msgstr "" msgstr ""
#: cps/web.py:1662 cps/web.py:1675 #: cps/web.py:1640 cps/web.py:1653
msgid "search" msgid "search"
msgstr "" msgstr ""
#: cps/templates/index.xml:43 cps/templates/index.xml:47 #: cps/templates/index.xml:42 cps/templates/index.xml:46
#: cps/templates/layout.html:127 cps/web.py:1751 #: cps/templates/layout.html:127 cps/web.py:1729
msgid "Read Books" msgid "Read Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:50 cps/templates/index.xml:54 #: cps/templates/index.xml:49 cps/templates/index.xml:53
#: cps/templates/layout.html:128 cps/web.py:1754 #: cps/templates/layout.html:128 cps/web.py:1732
msgid "Unread Books" msgid "Unread Books"
msgstr "" 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" msgid "Read a Book"
msgstr "" msgstr ""
#: cps/web.py:1888 cps/web.py:2513 #: cps/web.py:1868 cps/web.py:2493
msgid "Please fill out all fields!" msgid "Please fill out all fields!"
msgstr "" 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" msgid "register"
msgstr "" msgstr ""
#: cps/web.py:1904 #: cps/web.py:1884
msgid "An unknown error occured. Please try again later." msgid "An unknown error occured. Please try again later."
msgstr "" msgstr ""
#: cps/web.py:1909 #: cps/web.py:1889
msgid "This username or email address is already in use." msgid "This username or email address is already in use."
msgstr "" msgstr ""
#: cps/web.py:1928 cps/web.py:2024 #: cps/web.py:1908 cps/web.py:2004
#, python-format #, python-format
msgid "you are now logged in as: '%(nickname)s'" msgid "you are now logged in as: '%(nickname)s'"
msgstr "" msgstr ""
#: cps/web.py:1933 #: cps/web.py:1913
msgid "Wrong Username or Password" msgid "Wrong Username or Password"
msgstr "" msgstr ""
#: cps/web.py:1939 cps/web.py:1960 #: cps/web.py:1919 cps/web.py:1940
msgid "login" msgid "login"
msgstr "" msgstr ""
#: cps/web.py:1972 cps/web.py:2003 #: cps/web.py:1952 cps/web.py:1983
msgid "Token not found" msgid "Token not found"
msgstr "" msgstr ""
#: cps/web.py:1980 cps/web.py:2011 #: cps/web.py:1960 cps/web.py:1991
msgid "Token has expired" msgid "Token has expired"
msgstr "" msgstr ""
#: cps/web.py:1988 #: cps/web.py:1968
msgid "Success! Please return to your device" msgid "Success! Please return to your device"
msgstr "" msgstr ""
#: cps/web.py:2038 #: cps/web.py:2018
msgid "Please configure the SMTP mail settings first..." msgid "Please configure the SMTP mail settings first..."
msgstr "" msgstr ""
#: cps/web.py:2042 #: cps/web.py:2022
#, python-format #, python-format
msgid "Book successfully send to %(kindlemail)s" msgid "Book successfully send to %(kindlemail)s"
msgstr "" msgstr ""
#: cps/web.py:2046 #: cps/web.py:2026
#, python-format #, python-format
msgid "There was an error sending this book: %(res)s" msgid "There was an error sending this book: %(res)s"
msgstr "" 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..." msgid "Please configure your kindle email address first..."
msgstr "" msgstr ""
#: cps/web.py:2092 #: cps/web.py:2072
#, python-format #, python-format
msgid "Book has been added to shelf: %(sname)s" msgid "Book has been added to shelf: %(sname)s"
msgstr "" msgstr ""
#: cps/web.py:2127 #: cps/web.py:2107
#, python-format #, python-format
msgid "Book has been removed from shelf: %(sname)s" msgid "Book has been removed from shelf: %(sname)s"
msgstr "" msgstr ""
#: cps/web.py:2146 cps/web.py:2170 #: cps/web.py:2126 cps/web.py:2150
#, python-format #, python-format
msgid "A shelf with the name '%(title)s' already exists." msgid "A shelf with the name '%(title)s' already exists."
msgstr "" msgstr ""
#: cps/web.py:2151 #: cps/web.py:2131
#, python-format #, python-format
msgid "Shelf %(title)s created" msgid "Shelf %(title)s created"
msgstr "" msgstr ""
#: cps/web.py:2153 cps/web.py:2181 #: cps/web.py:2133 cps/web.py:2161
msgid "There was an error" msgid "There was an error"
msgstr "" msgstr ""
#: cps/web.py:2154 cps/web.py:2156 #: cps/web.py:2134 cps/web.py:2136
msgid "create a shelf" msgid "create a shelf"
msgstr "" msgstr ""
#: cps/web.py:2179 #: cps/web.py:2159
#, python-format #, python-format
msgid "Shelf %(title)s changed" msgid "Shelf %(title)s changed"
msgstr "" msgstr ""
#: cps/web.py:2182 cps/web.py:2184 #: cps/web.py:2162 cps/web.py:2164
msgid "Edit a shelf" msgid "Edit a shelf"
msgstr "" msgstr ""
#: cps/web.py:2204 #: cps/web.py:2184
#, python-format #, python-format
msgid "successfully deleted shelf %(name)s" msgid "successfully deleted shelf %(name)s"
msgstr "" msgstr ""
#: cps/web.py:2226 #: cps/web.py:2206
#, python-format #, python-format
msgid "Shelf: '%(name)s'" msgid "Shelf: '%(name)s'"
msgstr "" msgstr ""
#: cps/web.py:2229 #: cps/web.py:2209
msgid "Error opening shelf. Shelf does not exist or is not accessible" msgid "Error opening shelf. Shelf does not exist or is not accessible"
msgstr "" msgstr ""
#: cps/web.py:2261 #: cps/web.py:2241
#, python-format #, python-format
msgid "Change order of Shelf: '%(name)s'" msgid "Change order of Shelf: '%(name)s'"
msgstr "" msgstr ""
#: cps/web.py:2326 #: cps/web.py:2306
msgid "Found an existing account for this email address." msgid "Found an existing account for this email address."
msgstr "" msgstr ""
#: cps/web.py:2328 cps/web.py:2332 #: cps/web.py:2308 cps/web.py:2312
#, python-format #, python-format
msgid "%(name)s's profile" msgid "%(name)s's profile"
msgstr "" msgstr ""
#: cps/web.py:2329 #: cps/web.py:2309
msgid "Profile updated" msgid "Profile updated"
msgstr "" msgstr ""
#: cps/web.py:2343 #: cps/web.py:2323
msgid "Admin page" msgid "Admin page"
msgstr "" msgstr ""
#: cps/web.py:2467 #: cps/web.py:2447
msgid "Calibre-web configuration updated" msgid "Calibre-web configuration updated"
msgstr "" 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" msgid "Basic Configuration"
msgstr "" msgstr ""
#: cps/web.py:2478 #: cps/web.py:2458
msgid "DB location is not valid, please enter correct path" msgid "DB location is not valid, please enter correct path"
msgstr "" 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" msgid "Add new user"
msgstr "" msgstr ""
#: cps/web.py:2560 #: cps/web.py:2540
#, python-format #, python-format
msgid "User '%(user)s' created" msgid "User '%(user)s' created"
msgstr "" msgstr ""
#: cps/web.py:2564 #: cps/web.py:2544
msgid "Found an existing account for this email address or nickname." msgid "Found an existing account for this email address or nickname."
msgstr "" msgstr ""
#: cps/web.py:2586 #: cps/web.py:2566
msgid "Mail settings updated" msgid "Mail settings updated"
msgstr "" msgstr ""
#: cps/web.py:2593 #: cps/web.py:2573
#, python-format #, python-format
msgid "Test E-Mail successfully send to %(kindlemail)s" msgid "Test E-Mail successfully send to %(kindlemail)s"
msgstr "" msgstr ""
#: cps/web.py:2596 #: cps/web.py:2576
#, python-format #, python-format
msgid "There was an error sending the Test E-Mail: %(res)s" msgid "There was an error sending the Test E-Mail: %(res)s"
msgstr "" msgstr ""
#: cps/web.py:2600 #: cps/web.py:2580
msgid "E-Mail settings updated" msgid "E-Mail settings updated"
msgstr "" msgstr ""
#: cps/web.py:2601 #: cps/web.py:2581
msgid "Edit mail settings" msgid "Edit mail settings"
msgstr "" msgstr ""
#: cps/web.py:2630 #: cps/web.py:2610
#, python-format #, python-format
msgid "User '%(nick)s' deleted" msgid "User '%(nick)s' deleted"
msgstr "" msgstr ""
#: cps/web.py:2728 #: cps/web.py:2708
#, python-format #, python-format
msgid "User '%(nick)s' updated" msgid "User '%(nick)s' updated"
msgstr "" msgstr ""
#: cps/web.py:2731 #: cps/web.py:2711
msgid "An unknown error occured." msgid "An unknown error occured."
msgstr "" msgstr ""
#: cps/web.py:2734 #: cps/web.py:2714
#, python-format #, python-format
msgid "Edit User %(nick)s" msgid "Edit User %(nick)s"
msgstr "" msgstr ""
#: cps/web.py:2756 #: cps/web.py:2730
msgid "Error opening eBook. File does not exist or file is not accessible" msgid "Error opening eBook. File does not exist or file is not accessible"
msgstr "" 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" msgid "edit metadata"
msgstr "" msgstr ""
#: cps/web.py:2783 cps/web.py:2787 #: cps/web.py:2757 cps/web.py:2761
msgid "unknown" msgid "unknown"
msgstr "" msgstr ""
#: cps/web.py:2972 #: cps/web.py:2954
#, python-format #, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server" msgid "File extension \"%s\" is not allowed to be uploaded to this server"
msgstr "" msgstr ""
#: cps/web.py:2978 #: cps/web.py:2960
msgid "File to be uploaded must have an extension" msgid "File to be uploaded must have an extension"
msgstr "" msgstr ""
#: cps/web.py:2997 #: cps/web.py:2979
#, python-format #, python-format
msgid "Failed to create path %s (Permission denied)." msgid "Failed to create path %s (Permission denied)."
msgstr "" msgstr ""
#: cps/web.py:3002 #: cps/web.py:2984
#, python-format #, python-format
msgid "Failed to store file %s (Permission denied)." msgid "Failed to store file %s (Permission denied)."
msgstr "" msgstr ""
#: cps/web.py:3007 #: cps/web.py:2989
#, python-format #, python-format
msgid "Failed to delete file %s (Permission denied)." msgid "Failed to delete file %s (Permission denied)."
msgstr "" msgstr ""
@ -598,6 +598,18 @@ msgstr ""
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "" 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 #: cps/templates/book_edit.html:16
msgid "Delete Book" msgid "Delete Book"
msgstr "" msgstr ""
@ -606,12 +618,13 @@ msgstr ""
msgid "Book Title" msgid "Book Title"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:26 cps/templates/book_edit.html:188 #: cps/templates/book_edit.html:26 cps/templates/book_edit.html:208
#: cps/templates/search_form.html:10 #: cps/templates/book_edit.html:226 cps/templates/search_form.html:10
msgid "Author" msgid "Author"
msgstr "" 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" msgid "Description"
msgstr "" msgstr ""
@ -694,33 +707,33 @@ msgstr ""
msgid "Go!" msgid "Go!"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:168 #: cps/templates/book_edit.html:171
msgid "Click the cover to load metadata to the form" msgid "Click the cover to load metadata to the form"
msgstr "" 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..." msgid "Loading..."
msgstr "" 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" msgid "Close"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:186 #: cps/templates/book_edit.html:210 cps/templates/book_edit.html:227
msgid "Search error!" #: cps/templates/detail.html:125 cps/templates/search_form.html:14
msgid "Publisher"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:187 #: cps/templates/book_edit.html:215 cps/templates/book_edit.html:229
msgid "No Result! Please try anonther keyword." msgid "Source"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:189 cps/templates/detail.html:125 #: cps/templates/book_edit.html:224
#: cps/templates/search_form.html:14 msgid "Search error!"
msgid "Publisher"
msgstr "" msgstr ""
#: cps/templates/book_edit.html:191 #: cps/templates/book_edit.html:225
msgid "Source" msgid "No Result! Please try anonther keyword."
msgstr "" msgstr ""
#: cps/templates/config_edit.html:7 #: cps/templates/config_edit.html:7
@ -812,7 +825,7 @@ msgstr ""
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "" 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" msgid "Admin user"
msgstr "" msgstr ""
@ -913,6 +926,11 @@ msgstr ""
msgid "Next" msgid "Next"
msgstr "" 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 #: cps/templates/index.html:5
msgid "Discover (Random Books)" msgid "Discover (Random Books)"
msgstr "" msgstr ""
@ -921,52 +939,47 @@ msgstr ""
msgid "Start" msgid "Start"
msgstr "" msgstr ""
#: cps/templates/index.xml:7 cps/templates/layout.html:40 #: cps/templates/index.xml:14 cps/templates/layout.html:121
#: cps/templates/layout.html:41
msgid "Search"
msgstr ""
#: cps/templates/index.xml:15 cps/templates/layout.html:121
msgid "Hot Books" msgid "Hot Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:19 #: cps/templates/index.xml:18
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "" 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" msgid "Best rated Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:26 #: cps/templates/index.xml:25
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
msgstr "" msgstr ""
#: cps/templates/index.xml:29 #: cps/templates/index.xml:28
msgid "New Books" msgid "New Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:33 #: cps/templates/index.xml:32
msgid "The latest Books" msgid "The latest Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:40 #: cps/templates/index.xml:39
msgid "Show Random Books" msgid "Show Random Books"
msgstr "" msgstr ""
#: cps/templates/index.xml:57 cps/templates/layout.html:139 #: cps/templates/index.xml:56 cps/templates/layout.html:139
msgid "Authors" msgid "Authors"
msgstr "" msgstr ""
#: cps/templates/index.xml:61 #: cps/templates/index.xml:60
msgid "Books ordered by Author" msgid "Books ordered by Author"
msgstr "" msgstr ""
#: cps/templates/index.xml:68 #: cps/templates/index.xml:67
msgid "Books ordered by category" msgid "Books ordered by category"
msgstr "" msgstr ""
#: cps/templates/index.xml:75 #: cps/templates/index.xml:74
msgid "Books ordered by series" msgid "Books ordered by series"
msgstr "" msgstr ""
@ -1077,7 +1090,11 @@ msgstr ""
msgid "Calibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "" 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." msgid "Reflow text when sidebars are open."
msgstr "" msgstr ""
@ -1126,7 +1143,7 @@ msgid "No Results for:"
msgstr "" msgstr ""
#: cps/templates/search.html:7 #: cps/templates/search.html:7
msgid "Please try a diffrent Search" msgid "Please try a different search"
msgstr "" msgstr ""
#: cps/templates/search.html:9 #: cps/templates/search.html:9
@ -1218,43 +1235,43 @@ msgid "Show all"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:46 #: cps/templates/user_edit.html:46
msgid "Show mature content" msgid "Show random books"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:50 #: cps/templates/user_edit.html:50
msgid "Show random books" msgid "Show hot books"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:54 #: cps/templates/user_edit.html:54
msgid "Show hot books" msgid "Show best rated books"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:58 #: cps/templates/user_edit.html:58
msgid "Show best rated books" msgid "Show language selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:62 #: cps/templates/user_edit.html:62
msgid "Show language selection" msgid "Show series selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:66 #: cps/templates/user_edit.html:66
msgid "Show series selection" msgid "Show category selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:70 #: cps/templates/user_edit.html:70
msgid "Show category selection" msgid "Show author selection"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:74 #: cps/templates/user_edit.html:74
msgid "Show author selection" msgid "Show read and unread"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:78 #: cps/templates/user_edit.html:78
msgid "Show read and unread" msgid "Show random books in detail view"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:82 #: cps/templates/user_edit.html:90
msgid "Show random books in detail view" msgid "Show mature content"
msgstr "" msgstr ""
#: cps/templates/user_edit.html:123 #: cps/templates/user_edit.html:123

@ -11,4 +11,5 @@ PyYAML==3.12
rsa==3.4.2 rsa==3.4.2
six==1.10.0 six==1.10.0
uritemplate==3.0.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 - Bootstrap 3 HTML5 interface
- full graphical setup - full graphical setup
- User management - User management with fine grained per-user permissions
- Admin interface - 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 - OPDS feed for eBook reader apps
- Filter and search by titles, authors, tags, series and language - Filter and search by titles, authors, tags, series and language
- Create custom book collection (shelves) - 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 - Restrict eBook download to logged-in users
- Support for public user registration - Support for public user registration
- Send eBooks to Kindle devices with the click of a button - 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 - Upload new books in PDF, epub, fb2 format
- Support for Calibre custom columns - Support for Calibre custom columns
- Fine grained per-user permissions - Ability to hide content based on categories for certain users
- Self update capability - Self update capability
- "Magic Link" login to make it easy to log on eReaders - "Magic Link" login to make it easy to log on eReaders

Loading…
Cancel
Save