merge conflicts
This commit is contained in:
commit
f11b123686
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
web.py ident export-subst
|
|
@ -1,13 +0,0 @@
|
|||
[General]
|
||||
DB_ROOT =
|
||||
APP_DB_ROOT =
|
||||
MAIN_DIR =
|
||||
LOG_DIR =
|
||||
PORT = 8083
|
||||
NEWEST_BOOKS = 60
|
||||
[Advanced]
|
||||
TITLE_REGEX = ^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+
|
||||
DEVELOPMENT = 0
|
||||
PUBLIC_REG = 0
|
||||
UPLOADING = 0
|
||||
ANON_BROWSE = 0
|
51
cps.py
51
cps.py
|
@ -1,61 +1,30 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
from threading import Thread
|
||||
from multiprocessing import Queue
|
||||
import time
|
||||
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Insert local directories into path
|
||||
sys.path.insert(0, os.path.join(base_path, 'vendor'))
|
||||
|
||||
from cps import web
|
||||
from cps import config
|
||||
from tornado.wsgi import WSGIContainer
|
||||
from tornado.httpserver import HTTPServer
|
||||
from tornado.ioloop import IOLoop
|
||||
|
||||
global title_sort
|
||||
|
||||
|
||||
def start_calibreweb(messagequeue):
|
||||
web.global_queue = messagequeue
|
||||
if config.DEVELOPMENT:
|
||||
web.app.run(host="0.0.0.0", port=config.PORT, debug=True)
|
||||
if __name__ == '__main__':
|
||||
if web.ub.DEVELOPMENT:
|
||||
web.app.run(host="0.0.0.0", port=web.ub.config.config_port, debug=True)
|
||||
else:
|
||||
http_server = HTTPServer(WSGIContainer(web.app))
|
||||
http_server.listen(config.PORT)
|
||||
http_server.listen(web.ub.config.config_port)
|
||||
IOLoop.instance().start()
|
||||
print "Tornado finished"
|
||||
http_server.stop()
|
||||
|
||||
|
||||
def stop_calibreweb():
|
||||
# Close Database connections for user and data
|
||||
web.db.session.close()
|
||||
web.db.engine.dispose()
|
||||
web.ub.session.close()
|
||||
web.ub.engine.dispose()
|
||||
test=IOLoop.instance()
|
||||
test.add_callback(test.stop)
|
||||
print("Asked Tornado to exit")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if config.DEVELOPMENT:
|
||||
web.app.run(host="0.0.0.0",port=config.PORT, debug=True)
|
||||
if web.global_task == 0:
|
||||
print("Performing restart of Calibre-web")
|
||||
os.execl(sys.executable, sys.executable, *sys.argv)
|
||||
else:
|
||||
while True:
|
||||
q = Queue()
|
||||
t = Thread(target=start_calibreweb, args=(q,))
|
||||
t.start()
|
||||
while True: #watching queue, if there is no call than sleep, otherwise break
|
||||
if q.empty():
|
||||
time.sleep(1)
|
||||
else:
|
||||
break
|
||||
stop_calibreweb()
|
||||
t.join()
|
||||
|
||||
print("Performing shutdown of Calibre-web")
|
||||
sys.exit(0)
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import uploader
|
||||
import os
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
from configobj import ConfigObj
|
||||
|
||||
CONFIG_FILE = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "config.ini")
|
||||
CFG = ConfigObj(CONFIG_FILE)
|
||||
CFG.encoding = 'UTF-8'
|
||||
|
||||
|
||||
def CheckSection(sec):
|
||||
""" Check if INI section exists, if not create it """
|
||||
try:
|
||||
CFG[sec]
|
||||
return True
|
||||
except:
|
||||
CFG[sec] = {}
|
||||
return False
|
||||
|
||||
|
||||
def check_setting_str(config, cfg_name, item_name, def_val, log=True):
|
||||
try:
|
||||
my_val = config[cfg_name][item_name].decode('UTF-8')
|
||||
if my_val == u"":
|
||||
my_val = def_val
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
my_val = def_val
|
||||
try:
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
config[cfg_name] = {}
|
||||
config[cfg_name][item_name] = my_val
|
||||
return my_val
|
||||
|
||||
|
||||
def check_setting_int(config, cfg_name, item_name, def_val):
|
||||
try:
|
||||
my_val = int(config[cfg_name][item_name])
|
||||
except:
|
||||
my_val = def_val
|
||||
try:
|
||||
config[cfg_name][item_name] = my_val
|
||||
except:
|
||||
config[cfg_name] = {}
|
||||
config[cfg_name][item_name] = my_val
|
||||
return my_val
|
||||
|
||||
CheckSection('General')
|
||||
DB_ROOT = check_setting_str(CFG, 'General', 'DB_ROOT', "")
|
||||
APP_DB_ROOT = check_setting_str(CFG, 'General', 'APP_DB_ROOT', os.getcwd())
|
||||
MAIN_DIR = check_setting_str(CFG, 'General', 'MAIN_DIR', os.getcwd())
|
||||
LOG_DIR = check_setting_str(CFG, 'General', 'LOG_DIR', os.getcwd())
|
||||
PORT = check_setting_int(CFG, 'General', 'PORT', 8083)
|
||||
NEWEST_BOOKS = check_setting_str(CFG, 'General', 'NEWEST_BOOKS', 60)
|
||||
RANDOM_BOOKS = check_setting_int(CFG, 'General', 'RANDOM_BOOKS', 4)
|
||||
|
||||
CheckSection('Advanced')
|
||||
TITLE_REGEX = check_setting_str(CFG, 'Advanced', 'TITLE_REGEX', '^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
||||
DEVELOPMENT = bool(check_setting_int(CFG, 'Advanced', 'DEVELOPMENT', 0))
|
||||
PUBLIC_REG = bool(check_setting_int(CFG, 'Advanced', 'PUBLIC_REG', 0))
|
||||
UPLOADING = bool(check_setting_int(CFG, 'Advanced', 'UPLOADING', 0))
|
||||
ANON_BROWSE = bool(check_setting_int(CFG, 'Advanced', 'ANON_BROWSE', 0))
|
||||
|
||||
SYS_ENCODING = "UTF-8"
|
||||
|
||||
if DB_ROOT == "":
|
||||
print "Calibre database directory (DB_ROOT) is not configured"
|
||||
sys.exit(1)
|
||||
|
||||
configval = {"DB_ROOT": DB_ROOT, "APP_DB_ROOT": APP_DB_ROOT, "MAIN_DIR": MAIN_DIR, "LOG_DIR": LOG_DIR, "PORT": PORT,
|
||||
"NEWEST_BOOKS": NEWEST_BOOKS, "DEVELOPMENT": DEVELOPMENT, "TITLE_REGEX": TITLE_REGEX,
|
||||
"PUBLIC_REG": PUBLIC_REG, "UPLOADING": UPLOADING, "ANON_BROWSE": ANON_BROWSE}
|
||||
|
||||
|
||||
def save_config(configval):
|
||||
new_config = ConfigObj(encoding='UTF-8')
|
||||
new_config.filename = CONFIG_FILE
|
||||
new_config['General'] = {}
|
||||
new_config['General']['DB_ROOT'] = configval["DB_ROOT"]
|
||||
new_config['General']['APP_DB_ROOT'] = configval["APP_DB_ROOT"]
|
||||
new_config['General']['MAIN_DIR'] = configval["MAIN_DIR"]
|
||||
new_config['General']['LOG_DIR'] = configval["LOG_DIR"]
|
||||
new_config['General']['PORT'] = configval["PORT"]
|
||||
new_config['General']['NEWEST_BOOKS'] = configval["NEWEST_BOOKS"]
|
||||
new_config['Advanced'] = {}
|
||||
new_config['Advanced']['TITLE_REGEX'] = configval["TITLE_REGEX"]
|
||||
new_config['Advanced']['DEVELOPMENT'] = int(configval["DEVELOPMENT"])
|
||||
new_config['Advanced']['PUBLIC_REG'] = int(configval["PUBLIC_REG"])
|
||||
new_config['Advanced']['UPLOADING'] = int(configval["UPLOADING"])
|
||||
new_config['Advanced']['ANON_BROWSE'] = int(configval["ANON_BROWSE"])
|
||||
new_config.write()
|
||||
return "Saved"
|
||||
|
||||
save_config(configval)
|
163
cps/db.py
163
cps/db.py
|
@ -5,15 +5,24 @@ from sqlalchemy import *
|
|||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import *
|
||||
import os
|
||||
import config
|
||||
import re
|
||||
import ast
|
||||
from ub import config
|
||||
import ub
|
||||
|
||||
# calibre sort stuff
|
||||
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
|
||||
session = None
|
||||
cc_exceptions = None
|
||||
cc_classes = None
|
||||
cc_ids = None
|
||||
books_custom_column_links = None
|
||||
engine = None
|
||||
|
||||
|
||||
# user defined sort function for calibre databases (Series, etc.)
|
||||
def title_sort(title):
|
||||
# calibre sort stuff
|
||||
# config=Config()
|
||||
title_pat = re.compile(config.config_title_regex, re.IGNORECASE)
|
||||
match = title_pat.search(title)
|
||||
if match:
|
||||
prep = match.group(1)
|
||||
|
@ -21,59 +30,32 @@ def title_sort(title):
|
|||
return title.strip()
|
||||
|
||||
|
||||
dbpath = os.path.join(config.DB_ROOT, "metadata.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False)
|
||||
conn = engine.connect()
|
||||
conn.connection.create_function('title_sort', 1, title_sort)
|
||||
Base = declarative_base()
|
||||
|
||||
books_authors_link = Table('books_authors_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_tags_link = Table('books_tags_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_series_link = Table('books_series_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('series', Integer, ForeignKey('series.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('series', Integer, ForeignKey('series.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_ratings_link = Table('books_ratings_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
|
||||
)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
|
||||
)
|
||||
|
||||
books_languages_link = Table('books_languages_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
|
||||
)
|
||||
|
||||
cc = conn.execute("SELECT id, datatype FROM custom_columns")
|
||||
cc_ids = []
|
||||
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
|
||||
books_custom_column_links = {}
|
||||
cc_classes = {}
|
||||
for row in cc:
|
||||
if row.datatype not in cc_exceptions:
|
||||
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('value', Integer, ForeignKey('custom_column_' + str(row.id) + '.id'), primary_key=True)
|
||||
)
|
||||
cc_ids.append([row.id, row.datatype])
|
||||
if row.datatype == 'bool':
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'book': Column(Integer, ForeignKey('books.id')),
|
||||
'value': Column(Boolean)}
|
||||
else:
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'value': Column(String)}
|
||||
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
|
||||
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
|
||||
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
|
||||
)
|
||||
|
||||
|
||||
class Identifiers(Base):
|
||||
|
@ -243,9 +225,10 @@ class Books(Base):
|
|||
series = relationship('Series', secondary=books_series_link, backref='books')
|
||||
ratings = relationship('Ratings', secondary=books_ratings_link, backref='books')
|
||||
languages = relationship('Languages', secondary=books_languages_link, backref='books')
|
||||
identifiers=relationship('Identifiers', backref='books')
|
||||
identifiers = relationship('Identifiers', backref='books')
|
||||
|
||||
def __init__(self, title, sort, author_sort, timestamp, pubdate, series_index, last_modified, path, has_cover, authors, tags):
|
||||
def __init__(self, title, sort, author_sort, timestamp, pubdate, series_index, last_modified, path, has_cover,
|
||||
authors, tags): # ToDO check Authors and tags necessary
|
||||
self.title = title
|
||||
self.sort = sort
|
||||
self.author_sort = author_sort
|
||||
|
@ -260,15 +243,6 @@ class Books(Base):
|
|||
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
|
||||
self.timestamp, self.pubdate, self.series_index,
|
||||
self.last_modified, self.path, self.has_cover)
|
||||
for id in cc_ids:
|
||||
if id[1] == 'bool':
|
||||
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
|
||||
primaryjoin=(Books.id == cc_classes[id[0]].book),
|
||||
backref='books'))
|
||||
else:
|
||||
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
|
||||
secondary = books_custom_column_links[id[0]],
|
||||
backref='books'))
|
||||
|
||||
|
||||
class Custom_Columns(Base):
|
||||
|
@ -288,7 +262,76 @@ class Custom_Columns(Base):
|
|||
display_dict = ast.literal_eval(self.display)
|
||||
return display_dict
|
||||
|
||||
# Base.metadata.create_all(engine)
|
||||
Session = sessionmaker()
|
||||
Session.configure(bind=engine)
|
||||
session = Session()
|
||||
|
||||
def setup_db():
|
||||
global session
|
||||
global cc_exceptions
|
||||
global cc_classes
|
||||
global cc_ids
|
||||
global books_custom_column_links
|
||||
global engine
|
||||
|
||||
if config.config_calibre_dir is None or config.config_calibre_dir == u'':
|
||||
return False
|
||||
|
||||
dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False)
|
||||
try:
|
||||
conn = engine.connect()
|
||||
|
||||
except:
|
||||
content = ub.session.query(ub.Settings).first()
|
||||
content.config_calibre_dir = None
|
||||
content.db_configured = False
|
||||
ub.session.commit()
|
||||
config.loadSettings()
|
||||
return False
|
||||
content = ub.session.query(ub.Settings).first()
|
||||
content.db_configured = True
|
||||
ub.session.commit()
|
||||
config.loadSettings()
|
||||
conn.connection.create_function('title_sort', 1, title_sort)
|
||||
|
||||
cc = conn.execute("SELECT id, datatype FROM custom_columns")
|
||||
|
||||
cc_ids = []
|
||||
cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
|
||||
books_custom_column_links = {}
|
||||
cc_classes = {}
|
||||
for row in cc:
|
||||
if row.datatype not in cc_exceptions:
|
||||
books_custom_column_links[row.id] = Table('books_custom_column_' + str(row.id) + '_link', Base.metadata,
|
||||
Column('book', Integer, ForeignKey('books.id'),
|
||||
primary_key=True),
|
||||
Column('value', Integer,
|
||||
ForeignKey('custom_column_' + str(row.id) + '.id'),
|
||||
primary_key=True)
|
||||
)
|
||||
cc_ids.append([row.id, row.datatype])
|
||||
if row.datatype == 'bool':
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'book': Column(Integer, ForeignKey('books.id')),
|
||||
'value': Column(Boolean)}
|
||||
else:
|
||||
ccdict = {'__tablename__': 'custom_column_' + str(row.id),
|
||||
'id': Column(Integer, primary_key=True),
|
||||
'value': Column(String)}
|
||||
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
|
||||
|
||||
for id in cc_ids:
|
||||
if id[1] == 'bool':
|
||||
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
|
||||
primaryjoin=(
|
||||
Books.id == cc_classes[id[0]].book),
|
||||
backref='books'))
|
||||
else:
|
||||
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
|
||||
secondary=books_custom_column_links[id[0]],
|
||||
backref='books'))
|
||||
|
||||
# Base.metadata.create_all(engine)
|
||||
Session = sessionmaker()
|
||||
Session.configure(bind=engine)
|
||||
session = Session()
|
||||
return True
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import zipfile
|
||||
from lxml import etree
|
||||
import os
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from lxml import etree
|
||||
import os
|
||||
import uploader
|
||||
|
||||
|
||||
# ToDo: Check usage of original_file_name
|
||||
def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
|
||||
|
||||
ns = {
|
||||
|
|
235
cps/helper.py
235
cps/helper.py
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import db, ub
|
||||
import config
|
||||
import db
|
||||
import ub
|
||||
from flask import current_app as app
|
||||
import logging
|
||||
import smtplib
|
||||
|
@ -21,7 +21,7 @@ from email.MIMEText import MIMEText
|
|||
from email.generator import Generator
|
||||
from flask_babel import gettext as _
|
||||
import subprocess
|
||||
|
||||
import shutil
|
||||
|
||||
def update_download(book_id, user_id):
|
||||
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
|
||||
|
@ -33,11 +33,13 @@ def update_download(book_id, user_id):
|
|||
ub.session.commit()
|
||||
|
||||
|
||||
def make_mobi(book_id):
|
||||
def make_mobi(book_id, calibrepath):
|
||||
vendorpath = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) +
|
||||
os.sep + "../vendor" + os.sep))
|
||||
if sys.platform == "win32":
|
||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
|
||||
kindlegen = os.path.join(vendorpath, u"kindlegen.exe")
|
||||
else:
|
||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
|
||||
kindlegen = os.path.join(vendorpath, u"kindlegen")
|
||||
if not os.path.exists(kindlegen):
|
||||
app.logger.error("make_mobi: kindlegen binary not found in: %s" % kindlegen)
|
||||
return None
|
||||
|
@ -47,7 +49,7 @@ def make_mobi(book_id):
|
|||
app.logger.error("make_mobi: epub format not found for book id: %d" % book_id)
|
||||
return None
|
||||
|
||||
file_path = os.path.join(config.DB_ROOT, book.path, data.name)
|
||||
file_path = os.path.join(calibrepath, book.path, data.name)
|
||||
if os.path.exists(file_path + u".epub"):
|
||||
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
|
||||
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||
|
@ -79,26 +81,26 @@ def make_mobi(book_id):
|
|||
|
||||
class StderrLogger(object):
|
||||
|
||||
buffer=''
|
||||
buffer = ''
|
||||
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger('cps.web')
|
||||
|
||||
def write(self, message):
|
||||
if message=='\n':
|
||||
if message == '\n':
|
||||
self.logger.debug(self.buffer)
|
||||
self.buffer=''
|
||||
self.buffer = ''
|
||||
else:
|
||||
self.buffer=self.buffer+message
|
||||
self.buffer += message
|
||||
|
||||
def send_test_mail(kindle_mail):
|
||||
|
||||
def send_raw_email(kindle_mail, msg):
|
||||
settings = ub.get_mail_settings()
|
||||
msg = MIMEMultipart()
|
||||
|
||||
msg['From'] = settings["mail_from"]
|
||||
msg['To'] = kindle_mail
|
||||
msg['Subject'] = _('Calibre-web test email')
|
||||
text = _('This email has been sent via calibre web.')
|
||||
|
||||
use_ssl = settings.get('mail_use_ssl', 0)
|
||||
use_ssl = int(settings.get('mail_use_ssl', 0))
|
||||
|
||||
# convert MIME message to string
|
||||
fp = StringIO()
|
||||
|
@ -108,21 +110,19 @@ def send_test_mail(kindle_mail):
|
|||
|
||||
# send email
|
||||
try:
|
||||
timeout=600 # set timeout to 5mins
|
||||
timeout = 600 # set timeout to 5mins
|
||||
|
||||
org_stderr = smtplib.stderr
|
||||
smtplib.stderr = StderrLogger()
|
||||
|
||||
if int(use_ssl) == 2:
|
||||
if use_ssl == 2:
|
||||
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
||||
else:
|
||||
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
||||
mailserver.set_debuglevel(1)
|
||||
|
||||
if int(use_ssl) == 1:
|
||||
#mailserver.ehlo()
|
||||
if use_ssl == 1:
|
||||
mailserver.starttls()
|
||||
#mailserver.ehlo()
|
||||
|
||||
if settings["mail_password"]:
|
||||
mailserver.login(settings["mail_login"], settings["mail_password"])
|
||||
|
@ -138,28 +138,22 @@ def send_test_mail(kindle_mail):
|
|||
return None
|
||||
|
||||
|
||||
def send_mail(book_id, kindle_mail):
|
||||
def send_test_mail(kindle_mail):
|
||||
msg = MIMEMultipart()
|
||||
msg['Subject'] = _(u'Calibre-web test email')
|
||||
text = _(u'This email has been sent via calibre web.')
|
||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
||||
return send_raw_email(kindle_mail, msg)
|
||||
|
||||
|
||||
def send_mail(book_id, kindle_mail, calibrepath):
|
||||
"""Send email with attachments"""
|
||||
is_mobi = False
|
||||
is_azw = False
|
||||
is_azw3 = False
|
||||
is_epub = False
|
||||
is_pdf = False
|
||||
file_path = None
|
||||
settings = ub.get_mail_settings()
|
||||
# create MIME message
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = settings["mail_from"]
|
||||
msg['To'] = kindle_mail
|
||||
msg['Subject'] = _(u'Send to Kindle')
|
||||
text = _(u'This email has been sent via calibre web.')
|
||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
||||
|
||||
use_ssl = settings.get('mail_use_ssl', 0)
|
||||
|
||||
# attach files
|
||||
# msg.attach(self.get_attachment(file_path))
|
||||
|
||||
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)
|
||||
|
||||
|
@ -167,11 +161,11 @@ def send_mail(book_id, kindle_mail):
|
|||
|
||||
for entry in data:
|
||||
if entry.format == "MOBI":
|
||||
formats["mobi"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".mobi")
|
||||
formats["mobi"] = os.path.join(calibrepath, book.path, entry.name + ".mobi")
|
||||
if entry.format == "EPUB":
|
||||
formats["epub"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".epub")
|
||||
formats["epub"] = os.path.join(calibrepath, book.path, entry.name + ".epub")
|
||||
if entry.format == "PDF":
|
||||
formats["pdf"] = os.path.join(config.DB_ROOT, book.path, entry.name + ".pdf")
|
||||
formats["pdf"] = os.path.join(calibrepath, book.path, entry.name + ".pdf")
|
||||
|
||||
if len(formats) == 0:
|
||||
return _("Could not find any formats suitable for sending by email")
|
||||
|
@ -179,7 +173,7 @@ def send_mail(book_id, kindle_mail):
|
|||
if 'mobi' in formats:
|
||||
msg.attach(get_attachment(formats['mobi']))
|
||||
elif 'epub' in formats:
|
||||
filepath = make_mobi(book.id)
|
||||
filepath = make_mobi(book.id, calibrepath)
|
||||
if filepath is not None:
|
||||
msg.attach(get_attachment(filepath))
|
||||
elif filepath is None:
|
||||
|
@ -191,40 +185,7 @@ def send_mail(book_id, kindle_mail):
|
|||
else:
|
||||
return _("Could not find any formats suitable for sending by email")
|
||||
|
||||
# convert MIME message to string
|
||||
fp = StringIO()
|
||||
gen = Generator(fp, mangle_from_=False)
|
||||
gen.flatten(msg)
|
||||
msg = fp.getvalue()
|
||||
|
||||
# send email
|
||||
try:
|
||||
timeout=600 # set timeout to 5mins
|
||||
|
||||
org_stderr = smtplib.stderr
|
||||
smtplib.stderr = StderrLogger()
|
||||
|
||||
if int(use_ssl) == 2:
|
||||
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
||||
else:
|
||||
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
||||
mailserver.set_debuglevel(1)
|
||||
|
||||
if int(use_ssl) == 1:
|
||||
mailserver.starttls()
|
||||
|
||||
if settings["mail_password"]:
|
||||
mailserver.login(settings["mail_login"], settings["mail_password"])
|
||||
mailserver.sendmail(settings["mail_login"], kindle_mail, msg)
|
||||
mailserver.quit()
|
||||
|
||||
smtplib.stderr = org_stderr
|
||||
|
||||
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
|
||||
app.logger.error(traceback.print_exc())
|
||||
return _("Failed to send mail: %s" % str(e))
|
||||
|
||||
return None
|
||||
return send_raw_email(kindle_mail, msg)
|
||||
|
||||
|
||||
def get_attachment(file_path):
|
||||
|
@ -242,8 +203,7 @@ def get_attachment(file_path):
|
|||
return attachment
|
||||
except IOError:
|
||||
traceback.print_exc()
|
||||
message = (_('The requested file could not be read. Maybe wrong '\
|
||||
'permissions?'))
|
||||
message = (_('The requested file could not be read. Maybe wrong permissions?')) # ToDo: What is this?
|
||||
return None
|
||||
|
||||
|
||||
|
@ -253,7 +213,7 @@ def get_valid_filename(value, replace_whitespace=True):
|
|||
filename. Limits num characters to 128 max.
|
||||
"""
|
||||
value = value[:128]
|
||||
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
||||
# re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
||||
value = unicodedata.normalize('NFKD', value)
|
||||
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
||||
value = unicode(re_slugify.sub('', value).strip())
|
||||
|
@ -273,10 +233,10 @@ def get_normalized_author(value):
|
|||
return value
|
||||
|
||||
|
||||
def update_dir_stucture(book_id):
|
||||
def update_dir_stucture(book_id, calibrepath):
|
||||
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
|
||||
book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
|
||||
path = os.path.join(config.DB_ROOT, book.path)
|
||||
path = os.path.join(calibrepath, book.path)
|
||||
|
||||
authordir = book.path.split(os.sep)[0]
|
||||
new_authordir = get_valid_filename(book.authors[0].name, False)
|
||||
|
@ -290,7 +250,122 @@ def update_dir_stucture(book_id):
|
|||
book.path = book.path.split(os.sep)[0] + os.sep + new_titledir
|
||||
|
||||
if authordir != new_authordir:
|
||||
new_author_path = os.path.join(os.path.join(config.DB_ROOT, new_authordir), os.path.basename(path))
|
||||
new_author_path = os.path.join(os.path.join(calibrepath, new_authordir), os.path.basename(path))
|
||||
os.renames(path, new_author_path)
|
||||
book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def file_to_list(file):
|
||||
return [x.strip() for x in open(file, 'r') if not x.startswith('#EXT')]
|
||||
|
||||
def one_minus_two(one, two):
|
||||
return [x for x in one if x not in set(two)]
|
||||
|
||||
def reduce_dirs(delete_files, new_list):
|
||||
new_delete = []
|
||||
for file in delete_files:
|
||||
parts = file.split(os.sep)
|
||||
sub = ''
|
||||
for i in range(len(parts)):
|
||||
sub = os.path.join(sub, parts[i])
|
||||
if sub == '':
|
||||
sub = os.sep
|
||||
count = 0
|
||||
for song in new_list:
|
||||
if song.startswith(sub):
|
||||
count += 1
|
||||
break
|
||||
if count == 0:
|
||||
if sub != '\\':
|
||||
new_delete.append(sub)
|
||||
break
|
||||
return list(set(new_delete))
|
||||
|
||||
def reduce_files(remove_items, exclude_items):
|
||||
rf = []
|
||||
for item in remove_items:
|
||||
if not item in exclude_items:
|
||||
rf.append(item)
|
||||
return rf
|
||||
|
||||
def moveallfiles(root_src_dir, root_dst_dir):
|
||||
change_permissions = True
|
||||
if sys.platform == "win32" or sys.platform == "darwin":
|
||||
change_permissions=False
|
||||
else:
|
||||
app.logger.debug('Update on OS-System : '+sys.platform )
|
||||
#print('OS-System: '+sys.platform )
|
||||
new_permissions=os.stat(root_dst_dir)
|
||||
#print new_permissions
|
||||
for src_dir, dirs, files in os.walk(root_src_dir):
|
||||
dst_dir = src_dir.replace(root_src_dir, root_dst_dir, 1)
|
||||
if not os.path.exists(dst_dir):
|
||||
os.makedirs(dst_dir)
|
||||
#print('Create-Dir: '+dst_dir)
|
||||
if change_permissions:
|
||||
#print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
||||
os.chown(dst_dir,new_permissions.st_uid,new_permissions.st_gid)
|
||||
for file_ in files:
|
||||
src_file = os.path.join(src_dir, file_)
|
||||
dst_file = os.path.join(dst_dir, file_)
|
||||
if os.path.exists(dst_file):
|
||||
if change_permissions:
|
||||
permission=os.stat(dst_file)
|
||||
#print('Remove file before copy: '+dst_file)
|
||||
os.remove(dst_file)
|
||||
else:
|
||||
if change_permissions:
|
||||
permission=new_permissions
|
||||
shutil.move(src_file, dst_dir)
|
||||
#print('Move File '+src_file+' to '+dst_dir)
|
||||
if change_permissions:
|
||||
try:
|
||||
os.chown(dst_file, permission.st_uid, permission.st_uid)
|
||||
#print('Permissions: User '+str(new_permissions.st_uid)+' Group '+str(new_permissions.st_uid))
|
||||
except:
|
||||
e = sys.exc_info()
|
||||
#print('Fail '+str(dst_file)+' error: '+str(e))
|
||||
return
|
||||
|
||||
|
||||
def update_source(source,destination):
|
||||
# destination files
|
||||
old_list=list()
|
||||
exclude = (['vendor' + os.sep + 'kindlegen.exe','vendor' + os.sep + 'kindlegen','/app.db','vendor','/update.py'])
|
||||
for root, dirs, files in os.walk(destination, topdown=True):
|
||||
for name in files:
|
||||
old_list.append(os.path.join(root, name).replace(destination, ''))
|
||||
for name in dirs:
|
||||
old_list.append(os.path.join(root, name).replace(destination, ''))
|
||||
# source files
|
||||
new_list = list()
|
||||
for root, dirs, files in os.walk(source, topdown=True):
|
||||
for name in files:
|
||||
new_list.append(os.path.join(root, name).replace(source, ''))
|
||||
for name in dirs:
|
||||
new_list.append(os.path.join(root, name).replace(source, ''))
|
||||
|
||||
delete_files = one_minus_two(old_list, new_list)
|
||||
#print('raw delete list', delete_files)
|
||||
|
||||
rf= reduce_files(delete_files, exclude)
|
||||
#print('reduced delete list', rf)
|
||||
|
||||
remove_items = reduce_dirs(rf, new_list)
|
||||
#print('delete files', remove_items)
|
||||
|
||||
moveallfiles(source, destination)
|
||||
|
||||
for item in remove_items:
|
||||
item_path = os.path.join(destination, item[1:])
|
||||
if os.path.isdir(item_path):
|
||||
print("Delete dir "+ item_path)
|
||||
shutil.rmtree(item_path)
|
||||
else:
|
||||
try:
|
||||
print("Delete file "+ item_path)
|
||||
os.remove(item_path)
|
||||
except:
|
||||
print("Could not remove:"+item_path)
|
||||
shutil.rmtree(source, ignore_errors=True)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@
|
|||
<th>{{_('Passwd')}}</th>
|
||||
</tr>
|
||||
{% for user in content %}
|
||||
{% if not user.role_anonymous() or config.ANON_BROWSE %}
|
||||
{% if not user.role_anonymous() or config.config_anonbrowse %}
|
||||
<tr>
|
||||
<td><a href="{{url_for('edit_user', user_id=user.id)}}">{{user.nickname}}</a></td>
|
||||
<td>{{user.email}}</td>
|
||||
|
@ -56,7 +56,7 @@
|
|||
<h2>{{_('Configuration')}}</h2>
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>{{_('Log File')}}</th>
|
||||
<th>{{_('Calibre DB dir')}}</th>
|
||||
<th>{{_('Log Level')}}</th>
|
||||
<th>{{_('Port')}}</th>
|
||||
<th>{{_('Books per page')}}</th>
|
||||
|
@ -65,17 +65,90 @@
|
|||
<th>{{_('Anonymous browsing')}}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{config.LOG_DIR}}</td>
|
||||
<td>{{config.LOG_DIR}}</td>
|
||||
<td>{{config.PORT}}</td>
|
||||
<td>{{config.NEWEST_BOOKS}}</td>
|
||||
<td>{% if config.UPLOADING %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if config.PUBLIC_REG %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if config.ANON_BROWSE %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{{config.config_calibre_dir}}</td>
|
||||
<td>{{config.get_Log_Level()}}</td>
|
||||
<td>{{config.config_port}}</td>
|
||||
<td>{{config.config_books_per_page}}</td>
|
||||
<td>{% if config.config_uploading %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if config.config_public_reg %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
<td>{% if config.config_anonbrowse %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||
</table>
|
||||
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div>
|
||||
<h2>{{_('Administration')}}</h2>
|
||||
{% if not config.DEVELOPMENT %}
|
||||
<div class="btn btn-default"><a href="{{url_for('shutdown')}}">{{_('Restart Calibre-web')}}</a></div>
|
||||
{% if not development %}
|
||||
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</a></div>
|
||||
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</a></div>
|
||||
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</a></div>
|
||||
<a href="{{url_for('update')}}" class="btn btn-default hidden" id="perform_update">{{_('Perform Update')}}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div id="RestartDialog" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>{{_('Do you really want to restart Calibre-web?')}}</p>
|
||||
<button type="button" class="btn btn-default" id="restart" data-dismiss="modal">{{_('Ok')}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="ShutdownDialog" class="modal fade" role="dialog">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<!-- Modal content-->
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-info">
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p>{{_('Do you really want to stop Calibre-web?')}}</p>
|
||||
<button type="button" class="btn btn-default" id="shutdown" data-dismiss="modal">{{_('Ok')}}</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{{_('Back')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
<script type="text/javascript">
|
||||
$("#restart").click(function() {
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: "{{url_for('shutdown')}}",
|
||||
data: {"parameter":0},
|
||||
//data: data,
|
||||
success: function(data) {
|
||||
return alert(data.text);}
|
||||
});
|
||||
});
|
||||
$("#shutdown").click(function() {
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: "{{url_for('shutdown')}}",
|
||||
data: {"parameter":1},
|
||||
success: function(data) {
|
||||
return alert(data.text);}
|
||||
});
|
||||
});
|
||||
$("#check_for_update").click(function() {
|
||||
$("#check_for_update").html('Checking...');
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: "{{url_for('get_update_status')}}",
|
||||
success: function(data) {
|
||||
if (data.status == true) {
|
||||
$("#check_for_update").addClass('hidden');
|
||||
$("#perform_update").removeClass('hidden');
|
||||
}else{
|
||||
$("#check_for_update").html('{{_('Check for update')}}');
|
||||
};}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
61
cps/templates/config_edit.html
Normal file
61
cps/templates/config_edit.html
Normal file
|
@ -0,0 +1,61 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
<div class="discover">
|
||||
<h1>{{title}}</h1>
|
||||
<form role="form" method="POST" autocomplete="off">
|
||||
<div class="form-group required">
|
||||
<label for="config_calibre_dir">{{_('Location of Calibre database')}}</label>
|
||||
<input type="text" class="form-control" name="config_calibre_dir" id="config_calibre_dir" value="{% if content.config_calibre_dir != None %}{{ content.config_calibre_dir }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_port">{{_('Server Port')}}</label>
|
||||
<input type="number" min="1" max="65535" class="form-control" name="config_port" id="config_port" value="{% if content.config_port != None %}{{ content.config_port }}{% endif %}" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_calibre_web_title">{{_('Title')}}</label>
|
||||
<input type="text" class="form-control" name="config_calibre_web_title" id="config_calibre_web_title" value="{% if content.config_calibre_web_title != None %}{{ content.config_calibre_web_title }}{% endif %}" autocomplete="off" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_books_per_page">{{_('Books per page')}}</label>
|
||||
<input type="number" min="1" max="200" class="form-control" name="config_books_per_page" id="config_books_per_page" value="{% if content.config_books_per_page != None %}{{ content.config_books_per_page }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_random_books">{{_('No. of random books to show')}}</label>
|
||||
<input type="number" min="1" max="30" class="form-control" name="config_random_books" id="config_random_books" value="{% if content.config_random_books != None %}{{ content.config_random_books }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="config_title_regex">{{_('Regular expression for title sorting')}}</label>
|
||||
<input type="text" class="form-control" name="config_title_regex" id="config_title_regex" value="{% if content.config_title_regex != None %}{{ content.config_title_regex }}{% endif %}" autocomplete="off">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="config_log_level">{{_('Log Level')}}</label>
|
||||
<select name="config_log_level" id="config_log_level" class="form-control">
|
||||
<option value="10" {% if content.config_log_level == 10 %}selected{% endif %}>DEBUG</option>
|
||||
<option value="20" {% if content.config_log_level == 20 or content.config_log_level == None %}selected{% endif %}>INFO</option>
|
||||
<option value="30" {% if content.config_log_level == 30 %}selected{% endif %}>WARNING</option>
|
||||
<option value="40" {% if content.config_log_level == 40 %}selected{% endif %}>ERROR</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_uploading" name="config_uploading" {% if content.config_uploading %}checked{% endif %}>
|
||||
<label for="config_uploading">{{_('Enable uploading')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_anonbrowse" name="config_anonbrowse" {% if content.config_anonbrowse %}checked{% endif %}>
|
||||
<label for="config_anonbrowse">{{_('Enable anonymous browsing')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" id="config_public_reg" name="config_public_reg" {% if content.config_public_reg %}checked{% endif %}>
|
||||
<label for="config_public_reg">{{_('Enable public registration')}}</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">{{_('Submit')}}</button>
|
||||
{% if not origin %}
|
||||
<a href="{{ url_for('admin') }}" class="btn btn-default">{{_('Back')}}</a>
|
||||
{% endif %}
|
||||
{% if success %}
|
||||
<a href="{{ url_for('login') }}" class="btn btn-default">{{_('Login')}}</a>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -15,7 +15,7 @@
|
|||
<h2>{{entry.title}}</h2>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', id=author.id ) }}">{{author.name}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -37,7 +37,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if entry.series|length > 0 %}
|
||||
<p>{{_('Book')}} {{entry.series_index}} {{_('of')}} <a href="{{url_for('series', name=entry.series[0].name)}}">{{entry.series[0].name}}</a></p>
|
||||
<p>{{_('Book')}} {{entry.series_index}} {{_('of')}} <a href="{{url_for('series', id=entry.series[0].id)}}">{{entry.series[0].name}}</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% if entry.languages.__len__() > 0 %}
|
||||
|
@ -58,20 +58,21 @@
|
|||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if entry.tags|length > 0 %}
|
||||
<p>
|
||||
<div class="tags">
|
||||
<span class="glyphicon glyphicon-tags"></span>
|
||||
|
||||
{% for tag in entry.tags %}
|
||||
<a href="{{ url_for('category', name=tag.name) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
|
||||
<a href="{{ url_for('category', id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
|
||||
{%endfor%}
|
||||
|
||||
</div>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if entry.pubdate != '0101-01-01 00:00:00' %}
|
||||
<p>{{_('Publishing date')}}: {{entry.pubdate[:10]}} </p>
|
||||
{% endif %}
|
||||
{% if cc|length > 0 %}
|
||||
<p>
|
||||
<div class="custom_columns">
|
||||
|
@ -101,7 +102,7 @@
|
|||
{% endif %}
|
||||
|
||||
|
||||
{% if entry.comments|length > 0 %}
|
||||
{% if entry.comments|length > 0 and entry.comments[0].text|length > 0%}
|
||||
<h3>{{_('Description:')}}</h3>
|
||||
{{entry.comments[0].text|safe}}
|
||||
{% endif %}
|
||||
|
@ -191,7 +192,6 @@
|
|||
<div class="btn-toolbar" role="toolbar">
|
||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
||||
<a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-warning" role="button"><span class="glyphicon glyphicon-edit"></span> {{_('Edit metadata')}}</a>
|
||||
<!-- <a href="{{ url_for('edit_book', book_id=entry.id) }}" class="btn btn-sm btn-danger" role="button"><span class="glyphicon glyphicon-trash"></span> Delete</a> -->
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
<div class="cover">
|
||||
{% if entry.has_cover is defined %}
|
||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path) }}" />
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="meta">
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author"><a href="{{url_for('author', name=entry.authors[0].name) }}">{{entry.authors[0].name}}</a></p>
|
||||
<p class="author"><a href="{{url_for('author', name=entry.authors[0].name | urlencode) }}">{{entry.authors[0].name}}</a></p>
|
||||
{% if entry.ratings.__len__() > 0 %}
|
||||
<div class="rating">
|
||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
<link rel="search"
|
||||
href="{{url_for('feed_osd')}}"
|
||||
type="application/opensearchdescription+xml"/>
|
||||
<title>Calibre Web</title>
|
||||
<title>{{instance}}</title>
|
||||
<author>
|
||||
<name>Calibre Web</name>
|
||||
<name>{{instance}}</name>
|
||||
<uri>https://github.com/janeczku/calibre-web</uri>
|
||||
</author>
|
||||
|
||||
|
@ -60,28 +60,12 @@
|
|||
{% endfor %}
|
||||
</entry>
|
||||
{% endfor %}
|
||||
{% for author in authors %}
|
||||
<entry>
|
||||
<title>{{author.name}}</title>
|
||||
<id>{{ url_for('feed_author', id=author.id) }}</id>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_author', id=author.id)}}"/>
|
||||
<link type="application/atom+xml" href="{{url_for('feed_author', id=author.id)}}" rel="subsection"/>
|
||||
</entry>
|
||||
{% endfor %}
|
||||
{% for entry in categorys %}
|
||||
{% for entry in listelements %}
|
||||
<entry>
|
||||
<title>{{entry.name}}</title>
|
||||
<id>{{ url_for('feed_category', id=entry.id) }}</id>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_category', id=entry.id)}}"/>
|
||||
<link type="application/atom+xml" href="{{url_for('feed_category', id=entry.id)}}" rel="subsection"/>
|
||||
</entry>
|
||||
{% endfor %}
|
||||
{% for entry in series %}
|
||||
<entry>
|
||||
<title>{{entry.name}}</title>
|
||||
<id>{{ url_for('feed_series', id=entry.id) }}</id>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for('feed_series', id=entry.id)}}" />
|
||||
<link type="application/atom+xml" href="{{url_for('feed_series', id=entry.id)}}" rel="subsection"/>
|
||||
<id>{{ url_for(folder, id=entry.id) }}</id>
|
||||
<link type="application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for(folder, id=entry.id)}}"/>
|
||||
<link type="application/atom+xml" href="{{url_for(folder, id=entry.id)}}" rel="subsection"/>
|
||||
</entry>
|
||||
{% endfor %}
|
||||
</feed>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "layout.html" %}
|
||||
{% block body %}
|
||||
{% if g.user.show_random_books() %}
|
||||
{% if g.user.show_detail_random() %}
|
||||
<div class="discover">
|
||||
<h2>{{_('Discover (Random Books)')}}</h2>
|
||||
<div class="row">
|
||||
|
@ -18,7 +18,7 @@
|
|||
</div>
|
||||
<div class="meta">
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author"><a href="{{url_for('author', name=entry.authors[0].name) }}">{{entry.authors[0].name}}</a></p>
|
||||
<p class="author"><a href="{{url_for('author', id=entry.authors[0].id) }}">{{entry.authors[0].name}}</a></p>
|
||||
{% if entry.ratings.__len__() > 0 %}
|
||||
<div class="rating">
|
||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||
|
@ -55,7 +55,7 @@
|
|||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', id=author.id) }}">{{author.name}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||
<link rel="search" title="{{_('Search')}}" href="{{url_for('feed_osd')}}"
|
||||
type="application/opensearchdescription+xml"/>
|
||||
<title>Calibre Web</title>
|
||||
<title>{{instance}}</title>
|
||||
<author>
|
||||
<name>Calibre Web</name>
|
||||
<name>{{instance}}</name>
|
||||
<uri>https://github.com/janeczku/calibre-web</uri>
|
||||
</author>
|
||||
<entry>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>calibre web | {{title}}</title>
|
||||
<title>{{instance}} | {{title}}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% for entry in entries %}
|
||||
<div class="row">
|
||||
<div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div>
|
||||
<div class="col-xs-6"><a href="{{url_for(folder, name=entry[0].name)}}">{{entry[0].name}}</a></div>
|
||||
<div class="col-xs-6"><a href="{{url_for(folder, id=entry[0].id )}}">{{entry[0].name}}</a></div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<LongName>Calibre Web</LongName>
|
||||
<ShortName>Calibre Web</ShortName>
|
||||
<Description>Calibre Web ebook catalog</Description>
|
||||
<Developer>janeczku</Developer>
|
||||
<LongName>{{instance}}</LongName>
|
||||
<ShortName>{{instance}}</ShortName>
|
||||
<Description>{{_('instanceCalibre Web ebook catalog')}}</Description>
|
||||
<Developer>Janeczku</Developer>
|
||||
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
||||
<Url type="text/html"
|
||||
template="{{url_for('search')}}?query={searchTerms}"/>
|
||||
<Url type="application/atom+xml"
|
||||
template="{{url_for('feed_normal_search')}}?query={searchTerms}"/>
|
||||
<SyndicationRight>open</SyndicationRight>
|
||||
<Language>de-DE</Language>
|
||||
<Language>{{lang}}</Language>
|
||||
<OutputEncoding>UTF-8</OutputEncoding>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
</OpenSearchDescription>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
<!-- Full Screen -->
|
||||
<!--<script src="js/libs/screenfull.min.js"></script>-->
|
||||
<script src="{{ url_for('static', filename='js/libs/screenfull.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/screenfull.min.js') }}"></script>
|
||||
|
||||
<!-- Render -->
|
||||
<!--<script src="js/epub.min.js"></script>-->
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<div class="cover">
|
||||
{% if entry.has_cover is defined %}
|
||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path) }}" />
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author">
|
||||
{% for author in entry.authors %}
|
||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
||||
<a href="{{url_for('author', name=author.name | urlencode) }}">{{author.name}}</a>
|
||||
{% if not loop.last %}
|
||||
&
|
||||
{% endif %}
|
||||
|
@ -44,7 +44,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||
<LongName>Calibre Web</LongName>
|
||||
<ShortName>Calibre Web</ShortName>
|
||||
<Description>Calibre Web ebook catalog</Description>
|
||||
<Developer>janeczku</Developer>
|
||||
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
||||
|
||||
<Url type="text/html"
|
||||
template="{{url_for('search')}}?query={searchTerms}"/>
|
||||
|
||||
<Url type="application/atom+xml"
|
||||
template="{{url_for('feed_search')}}?query={searchTerms}"/>
|
||||
|
||||
<SyndicationRight>open</SyndicationRight>
|
||||
<Language>de-DE</Language>
|
||||
<OutputEncoding>UTF-8</OutputEncoding>
|
||||
<InputEncoding>UTF-8</InputEncoding>
|
||||
</OpenSearchDescription>
|
|
@ -15,13 +15,13 @@
|
|||
<div class="cover">
|
||||
{% if entry.has_cover is defined %}
|
||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path) }}" />
|
||||
<img src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="meta">
|
||||
<p class="title">{{entry.title|shortentitle}}</p>
|
||||
<p class="author"><a href="{{url_for('author', name=entry.authors[0].name) }}">{{entry.authors[0].name}}</a></p>
|
||||
<p class="author"><a href="{{url_for('author', name=entry.authors[0].name | urlencode) }}">{{entry.authors[0].name}}</a></p>
|
||||
{% if entry.ratings.__len__() > 0 %}
|
||||
<div class="rating">
|
||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||
|
@ -36,7 +36,6 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,19 +12,19 @@
|
|||
<tbody>
|
||||
<tr>
|
||||
<th>Python</th>
|
||||
<td>{{Versions['PythonVersion']}}</td>
|
||||
<td>{{versions['PythonVersion']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Kindlegen</th>
|
||||
<td>{{Versions['KindlegenVersion']}}</td>
|
||||
<td>{{versions['KindlegenVersion']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ImageMagick</th>
|
||||
<td>{{Versions['ImageVersion']}}</td>
|
||||
<td>{{versions['ImageVersion']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>PyPDF2</th>
|
||||
<td>{{Versions['PyPdfVersion']}}</td>
|
||||
<td>{{versions['PyPdfVersion']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -40,6 +40,14 @@
|
|||
<th>{{authorcounter}}</th>
|
||||
<td>{{_('Authors in this Library')}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{categorycounter}}</th>
|
||||
<td>{{_('Categories in this Library')}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{{seriecounter}}</th>
|
||||
<td>{{_('Series in this Library')}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
|
|
|
@ -27,39 +27,47 @@
|
|||
<label for="locale">{{_('Language')}}</label>
|
||||
<select name="locale" id="locale" class="form-control">
|
||||
{% for translation in translations %}
|
||||
<option value="{{translation}}" {% if translation|string == content.locale %}selected{% endif %}>{{ translation.display_name }}</option>
|
||||
<option value="{{translation.language}}" {% if translation.language == content.locale %}selected{% endif %} {% if new_user == 1 and loop.first %}selected{% endif %}>{{ translation.display_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="default_language">{{_('Show books with language')}}</label>
|
||||
<select name="default_language" id="default_language" class="form-control">
|
||||
<option value="all" >{{ _('Show all') }}</option>
|
||||
<option value="all" {% if new_user == 1 %}selected{% endif %}>{{ _('Show all') }}</option>
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.lang_code }}" {% if content.default_language == language.lang_code %}selected{% endif %}>{{ language.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_random" {% if content.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>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_hot" {% if content.hot_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_hot" id="show_hot" {% if content.show_hot_books() %}checked{% endif %}>
|
||||
<label for="show_hot">{{_('Show hot books')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_language" {% if content.language_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_language" id="show_language" {% if content.show_language() %}checked{% endif %}>
|
||||
<label for="show_language">{{_('Show language selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_series" {% if content.series_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_series" id="show_series" {% if content.show_series() %}checked{% endif %}>
|
||||
<label for="show_series">{{_('Show series selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_category" {% if content.category_books %}checked{% endif %}>
|
||||
<input type="checkbox" name="show_category" id="show_category" {% if content.show_category() %}checked{% endif %}>
|
||||
<label for="show_category">{{_('Show category selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_author" id="show_author" {% if content.show_author() %}checked{% endif %}>
|
||||
<label for="show_author">{{_('Show author selection')}}</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="checkbox" name="show_detail_random" id="show_detail_random" {% if content.show_detail_random() %}checked{% endif %}>
|
||||
<label for="show_detail_random">{{_('Show random books in detail view')}}</label>
|
||||
</div>
|
||||
|
||||
{% if g.user and g.user.role_admin() and not profile %}
|
||||
{% if not content.role_anonymous() %}
|
||||
|
@ -105,7 +113,7 @@
|
|||
{% for entry in downloads %}
|
||||
<div class="col-sm-2">
|
||||
<a class="pull-left" href="{{ url_for('show_book', id=entry.id) }}">
|
||||
<img class="media-object" width="100" src="{{ url_for('get_cover', cover_path=entry.path) }}" alt="...">
|
||||
<img class="media-object" width="100" src="{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}" alt="...">
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
@ -1,842 +0,0 @@
|
|||
# Chinese (Simplified, China) translations for PROJECT.
|
||||
# Copyright (C) 2017 ORGANIZATION
|
||||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-01-18 19:12+0100\n"
|
||||
"PO-Revision-Date: 2017-01-06 17:00+0800\n"
|
||||
"Last-Translator: dalin <dalin.lin@gmail.com>\n"
|
||||
"Language: zh_Hans_CN\n"
|
||||
"Language-Team: zh_Hans_CN <LL@li.org>\n"
|
||||
"Plural-Forms: nplurals=1; plural=0\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=utf-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.3.4\n"
|
||||
|
||||
#: cps/book_formats.py:109 cps/book_formats.py:113 cps/web.py:948
|
||||
msgid "not installed"
|
||||
msgstr "未安装"
|
||||
|
||||
#: cps/helper.py:98
|
||||
msgid "Calibre-web test email"
|
||||
msgstr "Calibre-web 测试邮件"
|
||||
|
||||
#: cps/helper.py:99 cps/helper.py:155
|
||||
msgid "This email has been sent via calibre web."
|
||||
msgstr "此邮件由calibre web发送"
|
||||
|
||||
#: cps/helper.py:136 cps/helper.py:225
|
||||
#, python-format
|
||||
msgid "Failed to send mail: %s"
|
||||
msgstr "发送邮件失败: %s"
|
||||
|
||||
#: cps/helper.py:154 cps/templates/detail.html:127
|
||||
msgid "Send to Kindle"
|
||||
msgstr "发送到Kindle"
|
||||
|
||||
#: cps/helper.py:177 cps/helper.py:192
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr "无法找到适合邮件发送的格式"
|
||||
|
||||
#: cps/helper.py:186
|
||||
msgid "Could not convert epub to mobi"
|
||||
msgstr "无法转换epub到mobi"
|
||||
|
||||
#: cps/helper.py:245
|
||||
msgid "The requested file could not be read. Maybe wrong permissions?"
|
||||
msgstr "无法读取所请求的文件。可能是权限不对?"
|
||||
|
||||
#: cps/ub.py:259
|
||||
msgid "Guest"
|
||||
msgstr "游客"
|
||||
|
||||
#: cps/web.py:742
|
||||
msgid "Latest Books"
|
||||
msgstr "最新书籍"
|
||||
|
||||
#: cps/web.py:767
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr "热门书籍(最多下载)"
|
||||
|
||||
#: cps/templates/index.xml:29 cps/web.py:775
|
||||
msgid "Random Books"
|
||||
msgstr "随机书籍"
|
||||
|
||||
#: cps/web.py:788
|
||||
msgid "Author list"
|
||||
msgstr "作者列表"
|
||||
|
||||
#: cps/web.py:805
|
||||
#, python-format
|
||||
msgid "Author: %(nam)s"
|
||||
msgstr "作者: %(nam)s"
|
||||
|
||||
#: cps/templates/index.xml:50 cps/web.py:818
|
||||
msgid "Series list"
|
||||
msgstr "丛书列表"
|
||||
|
||||
#: cps/web.py:829
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr "丛书: %(serie)s"
|
||||
|
||||
#: cps/web.py:831 cps/web.py:927 cps/web.py:1126 cps/web.py:1874
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr "无法打开电子书。 文件不存在或者文件不可访问:"
|
||||
|
||||
#: cps/web.py:862
|
||||
msgid "Available languages"
|
||||
msgstr "可用语言"
|
||||
|
||||
#: cps/web.py:877
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr "语言: %(name)s"
|
||||
|
||||
#: cps/templates/index.xml:43 cps/web.py:890
|
||||
msgid "Category list"
|
||||
msgstr "分类列表"
|
||||
|
||||
#: cps/web.py:900
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr "分类: %(name)s"
|
||||
|
||||
#: cps/web.py:956
|
||||
msgid "Statistics"
|
||||
msgstr "统计"
|
||||
|
||||
#: cps/web.py:965
|
||||
msgid "Server restarts"
|
||||
msgstr "重启服务器"
|
||||
|
||||
#: cps/web.py:1102 cps/web.py:1109 cps/web.py:1116 cps/web.py:1123
|
||||
msgid "Read a Book"
|
||||
msgstr "阅读一本书"
|
||||
|
||||
#: cps/web.py:1172 cps/web.py:1510
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr "请填写所有字段"
|
||||
|
||||
#: cps/web.py:1188
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr "发生一个未知错误。请稍后再试。"
|
||||
|
||||
#: cps/web.py:1193
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr "此用户名或邮箱已被使用。"
|
||||
|
||||
#: cps/web.py:1196
|
||||
msgid "register"
|
||||
msgstr "注册"
|
||||
|
||||
#: cps/web.py:1212
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr "您现在已以'%(nickname)s'身份登录"
|
||||
|
||||
#: cps/web.py:1216
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr "用户名或密码错误"
|
||||
|
||||
#: cps/web.py:1218
|
||||
msgid "login"
|
||||
msgstr "登录"
|
||||
|
||||
#: cps/web.py:1235
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr "请先配置SMTP邮箱..."
|
||||
|
||||
#: cps/web.py:1239
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr "此书已被成功发给 %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:1243
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr "发送这本书的时候出现错误: %(res)s"
|
||||
|
||||
#: cps/web.py:1245
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr "请先配置您的kindle电子邮箱地址..."
|
||||
|
||||
#: cps/web.py:1265
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr "此书已被添加到书架: %(sname)s"
|
||||
|
||||
#: cps/web.py:1286
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr "此书已从书架 %(sname)s 中删除"
|
||||
|
||||
#: cps/web.py:1304 cps/web.py:1325
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr "已存在书架 '%(title)s'。"
|
||||
|
||||
#: cps/web.py:1309
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr "书架 %(title)s 已被创建"
|
||||
|
||||
#: cps/web.py:1311 cps/web.py:1336
|
||||
msgid "There was an error"
|
||||
msgstr "发生错误"
|
||||
|
||||
#: cps/web.py:1312 cps/web.py:1314
|
||||
msgid "create a shelf"
|
||||
msgstr "创建书架"
|
||||
|
||||
#: cps/web.py:1334
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr "书架 %(title)s 已被修改"
|
||||
|
||||
#: cps/web.py:1337 cps/web.py:1339
|
||||
msgid "Edit a shelf"
|
||||
msgstr "编辑书架"
|
||||
|
||||
#: cps/web.py:1360
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr "成功删除书架 %(name)s"
|
||||
|
||||
#: cps/web.py:1381
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr "书架: '%(name)s'"
|
||||
|
||||
#: cps/web.py:1409
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr "修改书架 '%(name)s' 顺序"
|
||||
|
||||
#: cps/web.py:1469
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr "找到已使用此邮箱的账号。"
|
||||
|
||||
#: cps/web.py:1471 cps/web.py:1474
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr "%(name)s 的资料"
|
||||
|
||||
#: cps/web.py:1472
|
||||
msgid "Profile updated"
|
||||
msgstr "资料已更新"
|
||||
|
||||
#: cps/web.py:1483 cps/web.py:1491
|
||||
msgid "Admin page"
|
||||
msgstr "管理页"
|
||||
|
||||
#: cps/templates/admin.html:33 cps/web.py:1511
|
||||
msgid "Add new user"
|
||||
msgstr "添加新用户"
|
||||
|
||||
#: cps/web.py:1544
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr "用户 '%(user)s' 已被创建"
|
||||
|
||||
#: cps/web.py:1548
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr "已找到使用此邮箱或昵称的账号。"
|
||||
|
||||
#: cps/web.py:1568
|
||||
msgid "Mail settings updated"
|
||||
msgstr "邮箱设置已更新"
|
||||
|
||||
#: cps/web.py:1574
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr "测试邮件已成功发送到 %(kindlemail)s"
|
||||
|
||||
#: cps/web.py:1577
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr "发送测试邮件时发生错误: %(res)s"
|
||||
|
||||
#: cps/web.py:1578
|
||||
msgid "Edit mail settings"
|
||||
msgstr "编辑邮箱设置"
|
||||
|
||||
#: cps/web.py:1606
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr "用户 '%(nick)s' 已被删除"
|
||||
|
||||
#: cps/web.py:1661
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr "用户 '%(nick)s' 已被更新"
|
||||
|
||||
#: cps/web.py:1664
|
||||
msgid "An unknown error occured."
|
||||
msgstr "发生未知错误。"
|
||||
|
||||
#: cps/web.py:1666
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr "编辑用户 %(nick)s"
|
||||
|
||||
#: cps/web.py:1904
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr "创建路径 %s 失败(权限拒绝)。"
|
||||
|
||||
#: cps/web.py:1909
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr "存储文件 %s 失败(权限拒绝)。"
|
||||
|
||||
#: cps/web.py:1914
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr "删除文件 %s 失败(权限拒绝)。"
|
||||
|
||||
#: cps/templates/admin.html:4
|
||||
msgid "User list"
|
||||
msgstr "用户列表"
|
||||
|
||||
#: cps/templates/admin.html:7
|
||||
msgid "Nickname"
|
||||
msgstr "昵称"
|
||||
|
||||
#: cps/templates/admin.html:8
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:9
|
||||
msgid "Kindle"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:10
|
||||
msgid "DLS"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:11 cps/templates/layout.html:83
|
||||
msgid "Admin"
|
||||
msgstr "管理"
|
||||
|
||||
#: cps/templates/admin.html:12 cps/templates/detail.html:114
|
||||
msgid "Download"
|
||||
msgstr "下载"
|
||||
|
||||
#: cps/templates/admin.html:13 cps/templates/layout.html:76
|
||||
msgid "Upload"
|
||||
msgstr "上传"
|
||||
|
||||
#: cps/templates/admin.html:14
|
||||
msgid "Edit"
|
||||
msgstr "编辑"
|
||||
|
||||
#: cps/templates/admin.html:15
|
||||
msgid "Passwd"
|
||||
msgstr "修改密码"
|
||||
|
||||
#: cps/templates/admin.html:34
|
||||
msgid "SMTP mail settings"
|
||||
msgstr "SMTP设置"
|
||||
|
||||
#: cps/templates/admin.html:37 cps/templates/email_edit.html:7
|
||||
msgid "SMTP hostname"
|
||||
msgstr "SMTP地址"
|
||||
|
||||
#: cps/templates/admin.html:38
|
||||
msgid "SMTP port"
|
||||
msgstr "SMTP端口"
|
||||
|
||||
#: cps/templates/admin.html:39
|
||||
msgid "SSL"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:40 cps/templates/email_edit.html:23
|
||||
msgid "SMTP login"
|
||||
msgstr "SMTP用户名"
|
||||
|
||||
#: cps/templates/admin.html:41 cps/templates/email_edit.html:27
|
||||
msgid "SMTP password"
|
||||
msgstr "SMTP密码"
|
||||
|
||||
#: cps/templates/admin.html:42
|
||||
msgid "From mail"
|
||||
msgstr "来自邮箱"
|
||||
|
||||
#: cps/templates/admin.html:54
|
||||
msgid "Change SMTP settings"
|
||||
msgstr "修改SMTP设置"
|
||||
|
||||
#: cps/templates/admin.html:56
|
||||
msgid "Configuration"
|
||||
msgstr "配置"
|
||||
|
||||
#: cps/templates/admin.html:59
|
||||
msgid "Log File"
|
||||
msgstr "日志文件"
|
||||
|
||||
#: cps/templates/admin.html:60
|
||||
msgid "Log Level"
|
||||
msgstr "日志级别"
|
||||
|
||||
#: cps/templates/admin.html:61
|
||||
msgid "Port"
|
||||
msgstr "端口"
|
||||
|
||||
#: cps/templates/admin.html:62
|
||||
msgid "Books per page"
|
||||
msgstr "每页书籍数"
|
||||
|
||||
#: cps/templates/admin.html:63
|
||||
msgid "Uploading"
|
||||
msgstr "上传"
|
||||
|
||||
#: cps/templates/admin.html:64
|
||||
msgid "Public registration"
|
||||
msgstr "开放注册"
|
||||
|
||||
#: cps/templates/admin.html:65
|
||||
msgid "Anonymous browsing"
|
||||
msgstr "匿名浏览"
|
||||
|
||||
#: cps/templates/admin.html:76
|
||||
msgid "Administration"
|
||||
msgstr "管理"
|
||||
|
||||
#: cps/templates/admin.html:78
|
||||
msgid "Restart Calibre-web"
|
||||
msgstr "重启 Calibre-web"
|
||||
|
||||
#: cps/templates/detail.html:38
|
||||
msgid "Book"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:38
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:44
|
||||
msgid "language"
|
||||
msgstr "语言"
|
||||
|
||||
#: cps/templates/detail.html:103
|
||||
msgid "Description:"
|
||||
msgstr "简介:"
|
||||
|
||||
#: cps/templates/detail.html:131
|
||||
msgid "Read in browser"
|
||||
msgstr "在浏览器中阅读"
|
||||
|
||||
#: cps/templates/detail.html:151
|
||||
msgid "Add to shelf"
|
||||
msgstr "添加到书架"
|
||||
|
||||
#: cps/templates/detail.html:191
|
||||
msgid "Edit metadata"
|
||||
msgstr "编辑元数据"
|
||||
|
||||
#: cps/templates/edit_book.html:14 cps/templates/search_form.html:6
|
||||
msgid "Book Title"
|
||||
msgstr "书名"
|
||||
|
||||
#: cps/templates/edit_book.html:18 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr "作者"
|
||||
|
||||
#: cps/templates/edit_book.html:22
|
||||
msgid "Description"
|
||||
msgstr "简介"
|
||||
|
||||
#: cps/templates/edit_book.html:26 cps/templates/search_form.html:13
|
||||
msgid "Tags"
|
||||
msgstr "标签"
|
||||
|
||||
#: cps/templates/edit_book.html:31 cps/templates/layout.html:133
|
||||
#: cps/templates/search_form.html:33
|
||||
msgid "Series"
|
||||
msgstr "丛书"
|
||||
|
||||
#: cps/templates/edit_book.html:35
|
||||
msgid "Series id"
|
||||
msgstr "丛书ID"
|
||||
|
||||
#: cps/templates/edit_book.html:39
|
||||
msgid "Rating"
|
||||
msgstr "评分"
|
||||
|
||||
#: cps/templates/edit_book.html:43
|
||||
msgid "Cover URL (jpg)"
|
||||
msgstr "封面URL (jpg)"
|
||||
|
||||
#: cps/templates/edit_book.html:48 cps/templates/user_edit.html:27
|
||||
msgid "Language"
|
||||
msgstr "语言"
|
||||
|
||||
#: cps/templates/edit_book.html:59
|
||||
msgid "Yes"
|
||||
msgstr "确认"
|
||||
|
||||
#: cps/templates/edit_book.html:60
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:102
|
||||
msgid "view book after edit"
|
||||
msgstr "编辑后查看书籍"
|
||||
|
||||
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
|
||||
#: cps/templates/search_form.html:75 cps/templates/shelf_edit.html:15
|
||||
#: cps/templates/user_edit.html:97
|
||||
msgid "Submit"
|
||||
msgstr "提交"
|
||||
|
||||
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:36
|
||||
#: cps/templates/shelf_edit.html:17 cps/templates/shelf_order.html:12
|
||||
#: cps/templates/user_edit.html:99
|
||||
msgid "Back"
|
||||
msgstr "后退"
|
||||
|
||||
#: cps/templates/email_edit.html:11
|
||||
msgid "SMTP port (usually 25 for plain SMTP and 465 for SSL and 587 for STARTTLS)"
|
||||
msgstr "SMTP端口(明文SMTP通常是25, SSL加密的是465, STARTTLS的是587)"
|
||||
|
||||
#: cps/templates/email_edit.html:15
|
||||
msgid "Encryption"
|
||||
msgstr "加密方式"
|
||||
|
||||
#: cps/templates/email_edit.html:17
|
||||
msgid "None"
|
||||
msgstr "无"
|
||||
|
||||
#: cps/templates/email_edit.html:18
|
||||
msgid "STARTTLS"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/email_edit.html:19
|
||||
msgid "SSL/TLS"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/email_edit.html:31
|
||||
msgid "From e-mail"
|
||||
msgstr "来自邮箱"
|
||||
|
||||
#: cps/templates/email_edit.html:34
|
||||
msgid "Save settings"
|
||||
msgstr "保存设置"
|
||||
|
||||
#: cps/templates/email_edit.html:35
|
||||
msgid "Save settings and send Test E-Mail"
|
||||
msgstr "保存设置并发送测试邮件"
|
||||
|
||||
#: cps/templates/feed.xml:20
|
||||
msgid "Next"
|
||||
msgstr "下一个"
|
||||
|
||||
#: cps/templates/index.html:5
|
||||
msgid "Discover (Random Books)"
|
||||
msgstr "发现(随机书籍)"
|
||||
|
||||
#: cps/templates/index.xml:5
|
||||
msgid "Start"
|
||||
msgstr "开始"
|
||||
|
||||
#: cps/templates/index.xml:7 cps/templates/layout.html:61
|
||||
msgid "Search"
|
||||
msgstr "搜索"
|
||||
|
||||
#: cps/templates/index.xml:15 cps/templates/layout.html:124
|
||||
msgid "Hot Books"
|
||||
msgstr "热门书籍"
|
||||
|
||||
#: cps/templates/index.xml:19
|
||||
msgid "Popular publications from this catalog based on Rating."
|
||||
msgstr "此目录中的书籍是基于评分的热门出版物。"
|
||||
|
||||
#: cps/templates/index.xml:22 cps/templates/layout.html:122
|
||||
msgid "New Books"
|
||||
msgstr "新书"
|
||||
|
||||
#: cps/templates/index.xml:26
|
||||
msgid "The latest Books"
|
||||
msgstr "最新书籍"
|
||||
|
||||
#: cps/templates/index.xml:33
|
||||
msgid "Show Random Books"
|
||||
msgstr "显示随机书籍"
|
||||
|
||||
#: cps/templates/index.xml:36 cps/templates/layout.html:135
|
||||
msgid "Authors"
|
||||
msgstr "作者"
|
||||
|
||||
#: cps/templates/index.xml:40
|
||||
msgid "Books ordered by Author"
|
||||
msgstr "书籍按作者组织"
|
||||
|
||||
#: cps/templates/index.xml:47
|
||||
msgid "Books ordered by category"
|
||||
msgstr "书籍按分类组织"
|
||||
|
||||
#: cps/templates/index.xml:54
|
||||
msgid "Books ordered by series"
|
||||
msgstr "书籍按丛书组织"
|
||||
|
||||
#: cps/templates/layout.html:48
|
||||
msgid "Toggle navigation"
|
||||
msgstr "切换导航"
|
||||
|
||||
#: cps/templates/layout.html:63
|
||||
msgid "Go!"
|
||||
msgstr "走起!"
|
||||
|
||||
#: cps/templates/layout.html:66
|
||||
msgid "Advanced Search"
|
||||
msgstr "高级搜索"
|
||||
|
||||
#: cps/templates/layout.html:87
|
||||
msgid "Logout"
|
||||
msgstr "注销"
|
||||
|
||||
#: cps/templates/layout.html:91 cps/templates/login.html:4
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
#: cps/templates/layout.html:92 cps/templates/register.html:18
|
||||
msgid "Register"
|
||||
msgstr "注册"
|
||||
|
||||
#: cps/templates/layout.html:121
|
||||
msgid "Browse"
|
||||
msgstr "浏览"
|
||||
|
||||
#: cps/templates/layout.html:127
|
||||
msgid "Discover"
|
||||
msgstr "发现"
|
||||
|
||||
#: cps/templates/layout.html:130
|
||||
msgid "Categories"
|
||||
msgstr "分类"
|
||||
|
||||
#: cps/templates/layout.html:137 cps/templates/search_form.html:54
|
||||
msgid "Languages"
|
||||
msgstr "语言"
|
||||
|
||||
#: cps/templates/layout.html:140
|
||||
msgid "Public Shelves"
|
||||
msgstr "公开书架"
|
||||
|
||||
#: cps/templates/layout.html:144
|
||||
msgid "Your Shelves"
|
||||
msgstr "您的书架"
|
||||
|
||||
#: cps/templates/layout.html:149
|
||||
msgid "Create a Shelf"
|
||||
msgstr "创建书架"
|
||||
|
||||
#: cps/templates/layout.html:150
|
||||
msgid "About"
|
||||
msgstr "关于"
|
||||
|
||||
#: cps/templates/login.html:7 cps/templates/login.html:8
|
||||
#: cps/templates/register.html:7 cps/templates/user_edit.html:8
|
||||
msgid "Username"
|
||||
msgstr "用户名"
|
||||
|
||||
#: cps/templates/login.html:11 cps/templates/login.html:12
|
||||
#: cps/templates/register.html:11 cps/templates/user_edit.html:18
|
||||
msgid "Password"
|
||||
msgstr "密码"
|
||||
|
||||
#: cps/templates/login.html:16
|
||||
msgid "Remember me"
|
||||
msgstr "记住我"
|
||||
|
||||
#: cps/templates/read.html:136
|
||||
msgid "Reflow text when sidebars are open."
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/readpdf.html:29
|
||||
msgid "PDF.js viewer"
|
||||
msgstr "PDF.js查看器"
|
||||
|
||||
#: cps/templates/readtxt.html:6
|
||||
msgid "Basic txt Reader"
|
||||
msgstr "简单的txt阅读器"
|
||||
|
||||
#: cps/templates/register.html:4
|
||||
msgid "Register a new account"
|
||||
msgstr "注册新用户"
|
||||
|
||||
#: cps/templates/register.html:8
|
||||
msgid "Choose a username"
|
||||
msgstr "选择一个用户名"
|
||||
|
||||
#: cps/templates/register.html:12
|
||||
msgid "Choose a password"
|
||||
msgstr "选择一个密码"
|
||||
|
||||
#: cps/templates/register.html:15 cps/templates/user_edit.html:13
|
||||
msgid "Email address"
|
||||
msgstr "邮箱地址"
|
||||
|
||||
#: cps/templates/register.html:16
|
||||
msgid "Your email address"
|
||||
msgstr "您的邮箱地址"
|
||||
|
||||
#: cps/templates/search.html:6
|
||||
msgid "No Results for:"
|
||||
msgstr "找不到结果:"
|
||||
|
||||
#: cps/templates/search.html:7
|
||||
msgid "Please try a diffrent Search"
|
||||
msgstr "请尝试别的关键字"
|
||||
|
||||
#: cps/templates/search.html:9
|
||||
msgid "Results for:"
|
||||
msgstr "结果:"
|
||||
|
||||
#: cps/templates/search_form.html:23
|
||||
msgid "Exclude Tags"
|
||||
msgstr "排除标签"
|
||||
|
||||
#: cps/templates/search_form.html:43
|
||||
msgid "Exclude Series"
|
||||
msgstr "排除丛书"
|
||||
|
||||
#: cps/templates/search_form.html:64
|
||||
msgid "Exclude Languages"
|
||||
msgstr "排除语言"
|
||||
|
||||
#: cps/templates/shelf.html:6
|
||||
msgid "Delete this Shelf"
|
||||
msgstr "删除此书架"
|
||||
|
||||
#: cps/templates/shelf.html:7
|
||||
msgid "Edit Shelf name"
|
||||
msgstr "编辑书架名"
|
||||
|
||||
#: cps/templates/shelf.html:8 cps/templates/shelf_order.html:11
|
||||
msgid "Change order"
|
||||
msgstr "修改顺序"
|
||||
|
||||
#: cps/templates/shelf_edit.html:7
|
||||
msgid "Title"
|
||||
msgstr "标题"
|
||||
|
||||
#: cps/templates/shelf_edit.html:12
|
||||
msgid "should the shelf be public?"
|
||||
msgstr "要公开此书架吗?"
|
||||
|
||||
#: cps/templates/shelf_order.html:5
|
||||
msgid "Drag 'n drop to rearrange order"
|
||||
msgstr "通过拖拽进行重新排序"
|
||||
|
||||
#: cps/templates/stats.html:3
|
||||
msgid "Linked libraries"
|
||||
msgstr "链接库"
|
||||
|
||||
#: cps/templates/stats.html:8
|
||||
msgid "Program library"
|
||||
msgstr "程序库"
|
||||
|
||||
#: cps/templates/stats.html:9
|
||||
msgid "Installed Version"
|
||||
msgstr "已安装版本"
|
||||
|
||||
#: cps/templates/stats.html:32
|
||||
msgid "Calibre library statistics"
|
||||
msgstr "Calibre书库统计"
|
||||
|
||||
#: cps/templates/stats.html:37
|
||||
msgid "Books in this Library"
|
||||
msgstr "本书在此书库"
|
||||
|
||||
#: cps/templates/stats.html:41
|
||||
msgid "Authors in this Library"
|
||||
msgstr "个作者在此书库"
|
||||
|
||||
#: cps/templates/user_edit.html:23
|
||||
msgid "Kindle E-Mail"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:35
|
||||
msgid "Show books with language"
|
||||
msgstr "按语言显示书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:37
|
||||
msgid "Show all"
|
||||
msgstr "显示全部"
|
||||
|
||||
#: cps/templates/user_edit.html:45
|
||||
msgid "Show random books"
|
||||
msgstr "显示随机书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:49
|
||||
msgid "Show hot books"
|
||||
msgstr "显示热门书籍"
|
||||
|
||||
#: cps/templates/user_edit.html:53
|
||||
msgid "Show language selection"
|
||||
msgstr "显示语言选择"
|
||||
|
||||
#: cps/templates/user_edit.html:57
|
||||
msgid "Show series selection"
|
||||
msgstr "显示丛书选择"
|
||||
|
||||
#: cps/templates/user_edit.html:61
|
||||
msgid "Show category selection"
|
||||
msgstr "显示分类选择"
|
||||
|
||||
#: cps/templates/user_edit.html:68
|
||||
msgid "Admin user"
|
||||
msgstr "管理用户"
|
||||
|
||||
#: cps/templates/user_edit.html:73
|
||||
msgid "Allow Downloads"
|
||||
msgstr "允许下载"
|
||||
|
||||
#: cps/templates/user_edit.html:77
|
||||
msgid "Allow Uploads"
|
||||
msgstr "允许上传"
|
||||
|
||||
#: cps/templates/user_edit.html:81
|
||||
msgid "Allow Edit"
|
||||
msgstr "允许编辑"
|
||||
|
||||
#: cps/templates/user_edit.html:86
|
||||
msgid "Allow Changing Password"
|
||||
msgstr "允许修改密码"
|
||||
|
||||
#: cps/templates/user_edit.html:93
|
||||
msgid "Delete this user"
|
||||
msgstr "删除此用户"
|
||||
|
||||
#: cps/templates/user_edit.html:104
|
||||
msgid "Recent Downloads"
|
||||
msgstr "最近下载"
|
||||
|
||||
#~ msgid "SMTP port (usually 25 for plain SMTP and 587 for SSL)"
|
||||
#~ msgstr "SMTP端口(不加密的SMTP通常是25, SSL加密的是587)"
|
||||
|
||||
#~ msgid "Server uses SSL (StartTLS)"
|
||||
#~ msgstr "服务器使用SSL (StartTLS)"
|
||||
|
||||
#~ msgid "change order"
|
||||
#~ msgstr "修改顺序"
|
||||
|
||||
#~ msgid "Series in this Library"
|
||||
#~ msgstr "个丛书在此书库"
|
||||
|
||||
#~ msgid "Tags in this Library"
|
||||
#~ msgstr "个标签在此书库"
|
||||
|
||||
#~ msgid "Usercount for calibre web"
|
||||
#~ msgstr ""
|
||||
|
||||
#~ msgid "Latin"
|
||||
#~ msgstr ""
|
||||
|
223
cps/ub.py
223
cps/ub.py
|
@ -7,26 +7,42 @@ from sqlalchemy.ext.declarative import declarative_base
|
|||
from sqlalchemy.orm import *
|
||||
from flask_login import AnonymousUserMixin
|
||||
import os
|
||||
import config
|
||||
import traceback
|
||||
import logging
|
||||
from werkzeug.security import generate_password_hash
|
||||
from flask_babel import gettext as _
|
||||
|
||||
dbpath = os.path.join(config.APP_DB_ROOT, "app.db")
|
||||
dbpath = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + os.sep + ".." + os.sep), "app.db")
|
||||
engine = create_engine('sqlite:///{0}'.format(dbpath), echo=False)
|
||||
Base = declarative_base()
|
||||
|
||||
ROLE_USER = 0
|
||||
ROLE_ADMIN = 1
|
||||
ROLE_DOWNLOAD = 2
|
||||
ROLE_UPLOAD = 4
|
||||
ROLE_UPLOAD = 4
|
||||
ROLE_EDIT = 8
|
||||
ROLE_PASSWD = 16
|
||||
ROLE_ANONYMOUS = 32
|
||||
|
||||
DETAIL_RANDOM = 1
|
||||
SIDEBAR_LANGUAGE = 2
|
||||
SIDEBAR_SERIES = 4
|
||||
SIDEBAR_CATEGORY = 8
|
||||
SIDEBAR_HOT = 16
|
||||
SIDEBAR_RANDOM = 32
|
||||
SIDEBAR_AUTHOR = 64
|
||||
|
||||
DEFAULT_PASS = "admin123"
|
||||
|
||||
|
||||
class UserBase():
|
||||
|
||||
DEVELOPMENT = False
|
||||
|
||||
|
||||
|
||||
|
||||
class UserBase:
|
||||
@staticmethod
|
||||
def is_authenticated(self):
|
||||
return True
|
||||
|
||||
|
@ -79,25 +95,55 @@ class UserBase():
|
|||
return self.default_language
|
||||
|
||||
def show_random_books(self):
|
||||
return self.random_books
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_RANDOM == SIDEBAR_RANDOM else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_language(self):
|
||||
return self.language_books
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_LANGUAGE == SIDEBAR_LANGUAGE else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_hot_books(self):
|
||||
return self.hot_books
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_HOT == SIDEBAR_HOT else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_series(self):
|
||||
return self.series_books
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_SERIES == SIDEBAR_SERIES else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_category(self):
|
||||
return self.category_books
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_CATEGORY == SIDEBAR_CATEGORY else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_author(self):
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & SIDEBAR_AUTHOR == SIDEBAR_AUTHOR else False
|
||||
else:
|
||||
return False
|
||||
|
||||
def show_detail_random(self):
|
||||
if self.sidebar_view is not None:
|
||||
return True if self.sidebar_view & DETAIL_RANDOM == DETAIL_RANDOM else False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % self.nickname
|
||||
|
||||
|
||||
class User(UserBase,Base):
|
||||
# Baseclass for Users in Calibre-web, settings which are depending on certain users are stored here. It is derived from
|
||||
# User Base (all access methods are declared there)
|
||||
class User(UserBase, Base):
|
||||
__tablename__ = 'user'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
@ -109,30 +155,34 @@ class User(UserBase,Base):
|
|||
shelf = relationship('Shelf', backref='user', lazy='dynamic')
|
||||
downloads = relationship('Downloads', backref='user', lazy='dynamic')
|
||||
locale = Column(String(2), default="en")
|
||||
random_books = Column(Integer, default=1)
|
||||
language_books = Column(Integer, default=1)
|
||||
series_books = Column(Integer, default=1)
|
||||
category_books = Column(Integer, default=1)
|
||||
hot_books = Column(Integer, default=1)
|
||||
sidebar_view = Column(Integer, default=1)
|
||||
#language_books = Column(Integer, default=1)
|
||||
#series_books = Column(Integer, default=1)
|
||||
#category_books = Column(Integer, default=1)
|
||||
#hot_books = Column(Integer, default=1)
|
||||
default_language = Column(String(3), default="all")
|
||||
|
||||
|
||||
class Anonymous(AnonymousUserMixin,UserBase):
|
||||
# Class for anonymous user is derived from User base and complets overrides methods and properties for the
|
||||
# anonymous user
|
||||
class Anonymous(AnonymousUserMixin, UserBase):
|
||||
def __init__(self):
|
||||
self.loadSettings()
|
||||
|
||||
def loadSettings(self):
|
||||
data=session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first()
|
||||
data = session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first()
|
||||
settings = session.query(Settings).first()
|
||||
self.nickname = data.nickname
|
||||
self.role = data.role
|
||||
self.random_books = data.random_books
|
||||
self.sidebar_view = data.sidebar_view
|
||||
self.default_language = data.default_language
|
||||
self.language_books = data.language_books
|
||||
self.series_books = data.series_books
|
||||
self.category_books = data.category_books
|
||||
self.hot_books = data.hot_books
|
||||
#self.language_books = data.language_books
|
||||
#self.series_books = data.series_books
|
||||
#self.category_books = data.category_books
|
||||
#self.hot_books = data.hot_books
|
||||
self.default_language = data.default_language
|
||||
self.locale = data.locale
|
||||
self.anon_browse = settings.config_anonbrowse
|
||||
|
||||
def role_admin(self):
|
||||
return False
|
||||
|
@ -141,9 +191,10 @@ class Anonymous(AnonymousUserMixin,UserBase):
|
|||
return False
|
||||
|
||||
def is_anonymous(self):
|
||||
return config.ANON_BROWSE
|
||||
return self.anon_browse
|
||||
|
||||
|
||||
# Baseclass representing Shelfs in calibre-web inapp.db
|
||||
class Shelf(Base):
|
||||
__tablename__ = 'shelf'
|
||||
|
||||
|
@ -155,6 +206,8 @@ class Shelf(Base):
|
|||
def __repr__(self):
|
||||
return '<Shelf %r>' % self.name
|
||||
|
||||
|
||||
# Baseclass representing Relationship between books and Shelfs in Calibre-web in app.db (N:M)
|
||||
class BookShelf(Base):
|
||||
__tablename__ = 'book_shelf_link'
|
||||
|
||||
|
@ -167,6 +220,7 @@ class BookShelf(Base):
|
|||
return '<Book %r>' % self.id
|
||||
|
||||
|
||||
# Baseclass representing Downloads from calibre-web in app.db
|
||||
class Downloads(Base):
|
||||
__tablename__ = 'downloads'
|
||||
|
||||
|
@ -177,52 +231,133 @@ class Downloads(Base):
|
|||
def __repr__(self):
|
||||
return '<Download %r' % self.book_id
|
||||
|
||||
|
||||
# Baseclass for representing settings in app.db with email server settings and Calibre database settings
|
||||
# (application settings)
|
||||
class Settings(Base):
|
||||
__tablename__ = 'settings'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
mail_server = Column(String)
|
||||
mail_port = Column(Integer, default = 25)
|
||||
mail_use_ssl = Column(SmallInteger, default = 0)
|
||||
mail_port = Column(Integer, default=25)
|
||||
mail_use_ssl = Column(SmallInteger, default=0)
|
||||
mail_login = Column(String)
|
||||
mail_password = Column(String)
|
||||
mail_from = Column(String)
|
||||
config_calibre_dir = Column(String)
|
||||
config_port = Column(Integer, default=8083)
|
||||
config_calibre_web_title = Column(String, default=u'Calibre-web')
|
||||
config_books_per_page = Column(Integer, default=60)
|
||||
config_random_books = Column(Integer, default=4)
|
||||
config_title_regex = Column(String, default=u'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+')
|
||||
config_log_level = Column(SmallInteger, default=logging.INFO)
|
||||
config_uploading = Column(SmallInteger, default=0)
|
||||
config_anonbrowse = Column(SmallInteger, default=0)
|
||||
config_public_reg = Column(SmallInteger, default=0)
|
||||
|
||||
def __repr__(self):
|
||||
#return '<Smtp %r>' % (self.mail_server)
|
||||
pass
|
||||
|
||||
|
||||
# Class holds all application specific settings in calibre-web
|
||||
class Config:
|
||||
def __init__(self):
|
||||
self.config_main_dir = os.path.join(os.path.normpath(os.path.dirname(
|
||||
os.path.realpath(__file__)) + os.sep + ".." + os.sep))
|
||||
self.db_configured = None
|
||||
self.loadSettings()
|
||||
|
||||
def loadSettings(self):
|
||||
data = session.query(Settings).first()
|
||||
self.config_calibre_dir = data.config_calibre_dir
|
||||
self.config_port = data.config_port
|
||||
self.config_calibre_web_title = data.config_calibre_web_title
|
||||
self.config_books_per_page = data.config_books_per_page
|
||||
self.config_random_books = data.config_random_books
|
||||
self.config_title_regex = data.config_title_regex
|
||||
self.config_log_level = data.config_log_level
|
||||
self.config_uploading = data.config_uploading
|
||||
self.config_anonbrowse = data.config_anonbrowse
|
||||
self.config_public_reg = data.config_public_reg
|
||||
if self.config_calibre_dir is not None: # and (self.db_configured is None or self.db_configured is True):
|
||||
self.db_configured = True
|
||||
else:
|
||||
self.db_configured = False
|
||||
|
||||
@property
|
||||
def get_main_dir(self):
|
||||
return self.config_main_dir
|
||||
|
||||
def get_Log_Level(self):
|
||||
ret_value=""
|
||||
if self.config_log_level == logging.INFO:
|
||||
ret_value='INFO'
|
||||
elif self.config_log_level == logging.DEBUG:
|
||||
ret_value='DEBUG'
|
||||
elif self.config_log_level == logging.WARNING:
|
||||
ret_value='WARNING'
|
||||
elif self.config_log_level == logging.ERROR:
|
||||
ret_value='ERROR'
|
||||
return ret_value
|
||||
|
||||
|
||||
# Migrate database to current version, has to be updated after every database change. Currently migration from
|
||||
# everywhere to curent should work. Migration is done by checking if relevant coloums are existing, and than adding
|
||||
# rows with SQL commands
|
||||
def migrate_Database():
|
||||
if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
|
||||
create_anonymous_user()
|
||||
try:
|
||||
session.query(exists().where(User.random_books)).scalar()
|
||||
session.query(exists().where(User.locale)).scalar()
|
||||
session.commit()
|
||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||
conn = engine.connect()
|
||||
conn.execute("ALTER TABLE user ADD column random_books INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'")
|
||||
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
|
||||
session.commit()
|
||||
try:
|
||||
session.query(exists().where(User.language_books)).scalar()
|
||||
session.query(exists().where(Settings.config_calibre_dir)).scalar()
|
||||
session.commit()
|
||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||
conn = engine.connect()
|
||||
conn.execute("ALTER TABLE user ADD column language_books INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE user ADD column series_books INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE user ADD column category_books INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE user ADD column hot_books INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_calibre_dir` String")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_port` INTEGER DEFAULT 8083")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_calibre_web_title` String DEFAULT 'Calibre-web'")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_books_per_page` INTEGER DEFAULT 60")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_random_books` INTEGER DEFAULT 4")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_title_regex` String DEFAULT "
|
||||
"'^(A|The|An|Der|Die|Das|Den|Ein|Eine|Einen|Dem|Des|Einem|Eines)\s+'")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_log_level` SmallInteger DEFAULT " + str(logging.INFO))
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_uploading` SmallInteger DEFAULT 0")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_anonbrowse` SmallInteger DEFAULT 0")
|
||||
conn.execute("ALTER TABLE Settings ADD column `config_public_reg` SmallInteger DEFAULT 0")
|
||||
session.commit()
|
||||
try:
|
||||
session.query(exists().where(BookShelf.order)).scalar()
|
||||
session.commit()
|
||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||
conn = engine.connect()
|
||||
conn.execute("ALTER TABLE book_shelf_link ADD column `order` INTEGER DEFAULT 1")
|
||||
conn.execute("ALTER TABLE book_shelf_link ADD column 'order' INTEGER DEFAULT 1")
|
||||
session.commit()
|
||||
|
||||
try:
|
||||
create = False
|
||||
session.query(exists().where(User.sidebar_view)).scalar()
|
||||
session.commit()
|
||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||
conn = engine.connect()
|
||||
conn.execute("ALTER TABLE user ADD column `sidebar_view` Integer DEFAULT 1")
|
||||
session.commit()
|
||||
create=True
|
||||
try:
|
||||
if create:
|
||||
conn.execute("SELET language_books FROM user")
|
||||
session.commit()
|
||||
except exc.OperationalError:
|
||||
conn = engine.connect()
|
||||
conn.execute("UPDATE user SET 'sidebar_view' = (random_books*"+str(SIDEBAR_RANDOM)+"+ language_books *"+
|
||||
str(SIDEBAR_LANGUAGE)+"+ series_books *"+str(SIDEBAR_SERIES)+"+ category_books *"+str(SIDEBAR_CATEGORY)+
|
||||
"+ hot_books *"+str(SIDEBAR_HOT)+"+"+str(SIDEBAR_AUTHOR)+"+"+str(DETAIL_RANDOM)+")")
|
||||
session.commit()
|
||||
if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
|
||||
create_anonymous_user()
|
||||
|
||||
def create_default_config():
|
||||
settings = Settings()
|
||||
|
@ -254,10 +389,12 @@ def get_mail_settings():
|
|||
|
||||
return data
|
||||
|
||||
|
||||
# Generate user Guest (translated text), as anoymous user, no rights
|
||||
def create_anonymous_user():
|
||||
user = User()
|
||||
user.nickname = _("Guest")
|
||||
user.email='no@email'
|
||||
user.email = 'no@email'
|
||||
user.role = ROLE_ANONYMOUS
|
||||
user.password = generate_password_hash('1')
|
||||
|
||||
|
@ -269,10 +406,14 @@ def create_anonymous_user():
|
|||
pass
|
||||
|
||||
|
||||
# Generate User admin with admin123 password, and access to everything
|
||||
def create_admin_user():
|
||||
user = User()
|
||||
user.nickname = "admin"
|
||||
user.role = ROLE_USER + ROLE_ADMIN + ROLE_DOWNLOAD + ROLE_UPLOAD + ROLE_EDIT + ROLE_PASSWD
|
||||
user.sidebar_view = DETAIL_RANDOM + SIDEBAR_LANGUAGE + SIDEBAR_SERIES + SIDEBAR_CATEGORY + SIDEBAR_HOT + \
|
||||
SIDEBAR_RANDOM + SIDEBAR_AUTHOR
|
||||
|
||||
user.password = generate_password_hash(DEFAULT_PASS)
|
||||
|
||||
session.add(user)
|
||||
|
@ -282,10 +423,13 @@ def create_admin_user():
|
|||
session.rollback()
|
||||
pass
|
||||
|
||||
|
||||
# Open session for database connection
|
||||
Session = sessionmaker()
|
||||
Session.configure(bind=engine)
|
||||
session = Session()
|
||||
|
||||
# generate database and admin and guest user, if no database is existing
|
||||
if not os.path.exists(dbpath):
|
||||
try:
|
||||
Base.metadata.create_all(engine)
|
||||
|
@ -296,3 +440,6 @@ if not os.path.exists(dbpath):
|
|||
pass
|
||||
else:
|
||||
migrate_Database()
|
||||
|
||||
# Generate global Settings Object accecable from every file
|
||||
config = Config()
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
from tempfile import gettempdir
|
||||
import hashlib
|
||||
|
|
789
cps/web.py
789
cps/web.py
File diff suppressed because it is too large
Load Diff
352
messages.pot
352
messages.pot
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2017-01-18 19:12+0100\n"
|
||||
"POT-Creation-Date: 2017-01-28 20:35+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -17,284 +17,308 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.3.4\n"
|
||||
|
||||
#: cps/book_formats.py:109 cps/book_formats.py:113 cps/web.py:948
|
||||
#: cps/book_formats.py:109 cps/book_formats.py:113 cps/web.py:982
|
||||
msgid "not installed"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:98
|
||||
msgid "Calibre-web test email"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:99 cps/helper.py:155
|
||||
msgid "This email has been sent via calibre web."
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:136 cps/helper.py:225
|
||||
#: cps/helper.py:136
|
||||
#, python-format
|
||||
msgid "Failed to send mail: %s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:154 cps/templates/detail.html:127
|
||||
#: cps/helper.py:143
|
||||
msgid "Calibre-web test email"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:144 cps/helper.py:154
|
||||
msgid "This email has been sent via calibre web."
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:153 cps/templates/detail.html:129
|
||||
msgid "Send to Kindle"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:177 cps/helper.py:192
|
||||
#: cps/helper.py:171 cps/helper.py:186
|
||||
msgid "Could not find any formats suitable for sending by email"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:186
|
||||
#: cps/helper.py:180
|
||||
msgid "Could not convert epub to mobi"
|
||||
msgstr ""
|
||||
|
||||
#: cps/helper.py:245
|
||||
#: cps/helper.py:206
|
||||
msgid "The requested file could not be read. Maybe wrong permissions?"
|
||||
msgstr ""
|
||||
|
||||
#: cps/ub.py:259
|
||||
#: cps/ub.py:380
|
||||
msgid "Guest"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:742
|
||||
#: cps/web.py:774
|
||||
msgid "Latest Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:767
|
||||
#: cps/web.py:799
|
||||
msgid "Hot Books (most downloaded)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:29 cps/web.py:775
|
||||
#: cps/templates/index.xml:29 cps/web.py:808
|
||||
msgid "Random Books"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:788
|
||||
#: cps/web.py:821
|
||||
msgid "Author list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:805
|
||||
#: cps/web.py:838
|
||||
#, python-format
|
||||
msgid "Author: %(nam)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:50 cps/web.py:818
|
||||
#: cps/templates/index.xml:50 cps/web.py:851
|
||||
msgid "Series list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:829
|
||||
#: cps/web.py:862
|
||||
#, python-format
|
||||
msgid "Series: %(serie)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:831 cps/web.py:927 cps/web.py:1126 cps/web.py:1874
|
||||
#: cps/web.py:864 cps/web.py:961 cps/web.py:1179 cps/web.py:2041
|
||||
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:862
|
||||
#: cps/web.py:895
|
||||
msgid "Available languages"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:877
|
||||
#: cps/web.py:910
|
||||
#, python-format
|
||||
msgid "Language: %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/index.xml:43 cps/web.py:890
|
||||
#: cps/templates/index.xml:43 cps/web.py:923
|
||||
msgid "Category list"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:900
|
||||
#: cps/web.py:933
|
||||
#, python-format
|
||||
msgid "Category: %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:956
|
||||
#: cps/web.py:992
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:965
|
||||
msgid "Server restarts"
|
||||
#: cps/web.py:1013
|
||||
msgid "Performing Restart, please reload page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1102 cps/web.py:1109 cps/web.py:1116 cps/web.py:1123
|
||||
#: cps/web.py:1015
|
||||
msgid "Performing shutdown of server, please close window"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1091 cps/web.py:1104
|
||||
msgid "search"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1155 cps/web.py:1162 cps/web.py:1169 cps/web.py:1176
|
||||
msgid "Read a Book"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1172 cps/web.py:1510
|
||||
#: cps/web.py:1227 cps/web.py:1649
|
||||
msgid "Please fill out all fields!"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1188
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1193
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1196
|
||||
#: cps/web.py:1228 cps/web.py:1244 cps/web.py:1249 cps/web.py:1251
|
||||
msgid "register"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1212
|
||||
#: cps/web.py:1243
|
||||
msgid "An unknown error occured. Please try again later."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1248
|
||||
msgid "This username or email address is already in use."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1266
|
||||
#, python-format
|
||||
msgid "you are now logged in as: '%(nickname)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1216
|
||||
#: cps/web.py:1270
|
||||
msgid "Wrong Username or Password"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1218
|
||||
#: cps/web.py:1272
|
||||
msgid "login"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1235
|
||||
#: cps/web.py:1289
|
||||
msgid "Please configure the SMTP mail settings first..."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1239
|
||||
#: cps/web.py:1293
|
||||
#, python-format
|
||||
msgid "Book successfully send to %(kindlemail)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1243
|
||||
#: cps/web.py:1297
|
||||
#, python-format
|
||||
msgid "There was an error sending this book: %(res)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1245
|
||||
#: cps/web.py:1299
|
||||
msgid "Please configure your kindle email address first..."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1265
|
||||
#: cps/web.py:1319
|
||||
#, python-format
|
||||
msgid "Book has been added to shelf: %(sname)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1286
|
||||
#: cps/web.py:1340
|
||||
#, python-format
|
||||
msgid "Book has been removed from shelf: %(sname)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1304 cps/web.py:1325
|
||||
#: cps/web.py:1359 cps/web.py:1383
|
||||
#, python-format
|
||||
msgid "A shelf with the name '%(title)s' already exists."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1309
|
||||
#: cps/web.py:1364
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s created"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1311 cps/web.py:1336
|
||||
#: cps/web.py:1366 cps/web.py:1394
|
||||
msgid "There was an error"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1312 cps/web.py:1314
|
||||
#: cps/web.py:1367 cps/web.py:1369
|
||||
msgid "create a shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1334
|
||||
#: cps/web.py:1392
|
||||
#, python-format
|
||||
msgid "Shelf %(title)s changed"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1337 cps/web.py:1339
|
||||
#: cps/web.py:1395 cps/web.py:1397
|
||||
msgid "Edit a shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1360
|
||||
#: cps/web.py:1415
|
||||
#, python-format
|
||||
msgid "successfully deleted shelf %(name)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1381
|
||||
#: cps/web.py:1437
|
||||
#, python-format
|
||||
msgid "Shelf: '%(name)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1409
|
||||
#: cps/web.py:1468
|
||||
#, python-format
|
||||
msgid "Change order of Shelf: '%(name)s'"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1469
|
||||
#: cps/web.py:1528
|
||||
msgid "Found an existing account for this email address."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1471 cps/web.py:1474
|
||||
#: cps/web.py:1530 cps/web.py:1534
|
||||
#, python-format
|
||||
msgid "%(name)s's profile"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1472
|
||||
#: cps/web.py:1531
|
||||
msgid "Profile updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1483 cps/web.py:1491
|
||||
#: cps/web.py:1544
|
||||
msgid "Admin page"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:33 cps/web.py:1511
|
||||
#: cps/web.py:1604
|
||||
msgid "Calibre-web configuration updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1611 cps/web.py:1617 cps/web.py:1630
|
||||
msgid "Basic Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1615
|
||||
msgid "DB location is not valid, please enter correct path"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:33 cps/web.py:1651 cps/web.py:1693
|
||||
msgid "Add new user"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1544
|
||||
#: cps/web.py:1687
|
||||
#, python-format
|
||||
msgid "User '%(user)s' created"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1548
|
||||
#: cps/web.py:1691
|
||||
msgid "Found an existing account for this email address or nickname."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1568
|
||||
#: cps/web.py:1711
|
||||
msgid "Mail settings updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1574
|
||||
#: cps/web.py:1717
|
||||
#, python-format
|
||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1577
|
||||
#: cps/web.py:1720
|
||||
#, python-format
|
||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1578
|
||||
#: cps/web.py:1721
|
||||
msgid "Edit mail settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1606
|
||||
#: cps/web.py:1749
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' deleted"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1661
|
||||
#: cps/web.py:1825
|
||||
#, python-format
|
||||
msgid "User '%(nick)s' updated"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1664
|
||||
#: cps/web.py:1828
|
||||
msgid "An unknown error occured."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1666
|
||||
#: cps/web.py:1831
|
||||
#, python-format
|
||||
msgid "Edit User %(nick)s"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1904
|
||||
#: cps/web.py:2036 cps/web.py:2039 cps/web.py:2113
|
||||
msgid "edit metadata"
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:2071
|
||||
#, python-format
|
||||
msgid "Failed to create path %s (Permission denied)."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1909
|
||||
#: cps/web.py:2076
|
||||
#, python-format
|
||||
msgid "Failed to store file %s (Permission denied)."
|
||||
msgstr ""
|
||||
|
||||
#: cps/web.py:1914
|
||||
#: cps/web.py:2081
|
||||
#, python-format
|
||||
msgid "Failed to delete file %s (Permission denied)."
|
||||
msgstr ""
|
||||
|
@ -323,7 +347,7 @@ msgstr ""
|
|||
msgid "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:12 cps/templates/detail.html:114
|
||||
#: cps/templates/admin.html:12 cps/templates/detail.html:116
|
||||
msgid "Download"
|
||||
msgstr ""
|
||||
|
||||
|
@ -371,15 +395,15 @@ msgstr ""
|
|||
msgid "Change SMTP settings"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:56
|
||||
#: cps/templates/admin.html:56 cps/templates/admin.html:76
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:59
|
||||
msgid "Log File"
|
||||
msgid "Calibre DB dir"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:60
|
||||
#: cps/templates/admin.html:60 cps/templates/config_edit.html:32
|
||||
msgid "Log Level"
|
||||
msgstr ""
|
||||
|
||||
|
@ -387,7 +411,7 @@ msgstr ""
|
|||
msgid "Port"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:62
|
||||
#: cps/templates/admin.html:62 cps/templates/config_edit.html:19
|
||||
msgid "Books per page"
|
||||
msgstr ""
|
||||
|
||||
|
@ -403,101 +427,155 @@ msgstr ""
|
|||
msgid "Anonymous browsing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:76
|
||||
#: cps/templates/admin.html:77
|
||||
msgid "Administration"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/admin.html:78
|
||||
#: cps/templates/admin.html:79
|
||||
msgid "Restart Calibre-web"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:38
|
||||
msgid "Book"
|
||||
#: cps/templates/admin.html:80
|
||||
msgid "Stop Calibre-web"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:38
|
||||
msgid "of"
|
||||
#: cps/templates/admin.html:91
|
||||
msgid "Do you really want to restart Calibre-web?"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:44
|
||||
msgid "language"
|
||||
#: cps/templates/admin.html:92 cps/templates/admin.html:107
|
||||
msgid "Ok"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:103
|
||||
msgid "Description:"
|
||||
#: cps/templates/admin.html:93 cps/templates/admin.html:108
|
||||
#: cps/templates/book_edit.html:108 cps/templates/config_edit.html:54
|
||||
#: cps/templates/email_edit.html:36 cps/templates/shelf_edit.html:17
|
||||
#: cps/templates/shelf_order.html:12 cps/templates/user_edit.html:107
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:131
|
||||
msgid "Read in browser"
|
||||
#: cps/templates/admin.html:106
|
||||
msgid "Do you really want to stop Calibre-web?"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:151
|
||||
msgid "Add to shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:191
|
||||
msgid "Edit metadata"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:14 cps/templates/search_form.html:6
|
||||
#: cps/templates/book_edit.html:16 cps/templates/search_form.html:6
|
||||
msgid "Book Title"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:18 cps/templates/search_form.html:10
|
||||
#: cps/templates/book_edit.html:20 cps/templates/search_form.html:10
|
||||
msgid "Author"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:22
|
||||
#: cps/templates/book_edit.html:24
|
||||
msgid "Description"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:26 cps/templates/search_form.html:13
|
||||
#: cps/templates/book_edit.html:28 cps/templates/search_form.html:13
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:31 cps/templates/layout.html:133
|
||||
#: cps/templates/book_edit.html:33 cps/templates/layout.html:133
|
||||
#: cps/templates/search_form.html:33
|
||||
msgid "Series"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:35
|
||||
#: cps/templates/book_edit.html:37
|
||||
msgid "Series id"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:39
|
||||
#: cps/templates/book_edit.html:41
|
||||
msgid "Rating"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:43
|
||||
#: cps/templates/book_edit.html:45
|
||||
msgid "Cover URL (jpg)"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:48 cps/templates/user_edit.html:27
|
||||
#: cps/templates/book_edit.html:50 cps/templates/user_edit.html:27
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:59
|
||||
#: cps/templates/book_edit.html:61
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:60
|
||||
#: cps/templates/book_edit.html:62
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:102
|
||||
#: cps/templates/book_edit.html:104
|
||||
msgid "view book after edit"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
|
||||
#: cps/templates/search_form.html:75 cps/templates/shelf_edit.html:15
|
||||
#: cps/templates/user_edit.html:97
|
||||
#: cps/templates/book_edit.html:107 cps/templates/config_edit.html:52
|
||||
#: cps/templates/login.html:19 cps/templates/search_form.html:75
|
||||
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:105
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:36
|
||||
#: cps/templates/shelf_edit.html:17 cps/templates/shelf_order.html:12
|
||||
#: cps/templates/user_edit.html:99
|
||||
msgid "Back"
|
||||
#: cps/templates/config_edit.html:7
|
||||
msgid "Location of Calibre database"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:11
|
||||
msgid "Server Port"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:15 cps/templates/shelf_edit.html:7
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:23
|
||||
msgid "No. of random books to show"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:28
|
||||
msgid "Regular expression for title sorting"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:42
|
||||
msgid "Enable uploading"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:46
|
||||
msgid "Enable anonymous browsing"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:50
|
||||
msgid "Enable public registration"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/config_edit.html:57 cps/templates/layout.html:91
|
||||
#: cps/templates/login.html:4
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:40
|
||||
msgid "Book"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:40
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:46
|
||||
msgid "language"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:105
|
||||
msgid "Description:"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:133
|
||||
msgid "Read in browser"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:153
|
||||
msgid "Add to shelf"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/detail.html:193
|
||||
msgid "Edit metadata"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/email_edit.html:11
|
||||
|
@ -600,10 +678,6 @@ msgstr ""
|
|||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/layout.html:91 cps/templates/login.html:4
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/layout.html:92 cps/templates/register.html:18
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
|
@ -722,10 +796,6 @@ msgstr ""
|
|||
msgid "Change order"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/shelf_edit.html:7
|
||||
msgid "Title"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/shelf_edit.html:12
|
||||
msgid "should the shelf be public?"
|
||||
msgstr ""
|
||||
|
@ -790,31 +860,39 @@ msgstr ""
|
|||
msgid "Show category selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:68
|
||||
#: cps/templates/user_edit.html:65
|
||||
msgid "Show author selection"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:69
|
||||
msgid "Show random books in detail view"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:76
|
||||
msgid "Admin user"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:73
|
||||
#: cps/templates/user_edit.html:81
|
||||
msgid "Allow Downloads"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:77
|
||||
#: cps/templates/user_edit.html:85
|
||||
msgid "Allow Uploads"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:81
|
||||
#: cps/templates/user_edit.html:89
|
||||
msgid "Allow Edit"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:86
|
||||
#: cps/templates/user_edit.html:94
|
||||
msgid "Allow Changing Password"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:93
|
||||
#: cps/templates/user_edit.html:101
|
||||
msgid "Delete this user"
|
||||
msgstr ""
|
||||
|
||||
#: cps/templates/user_edit.html:104
|
||||
#: cps/templates/user_edit.html:112
|
||||
msgid "Recent Downloads"
|
||||
msgstr ""
|
||||
|
||||
|
|
28
readme.md
28
readme.md
|
@ -8,6 +8,7 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
|||
|
||||
##Features
|
||||
- Bootstrap 3 HTML5 interface
|
||||
- full graphical setup
|
||||
- User management
|
||||
- Admin interface
|
||||
- User Interface in english, french, german, simplified chinese, spanish
|
||||
|
@ -23,12 +24,14 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
|||
- Upload new books in PDF, epub, fb2 format
|
||||
- Support for Calibre custom columns
|
||||
- Fine grained per-user permissions
|
||||
- Self update capability
|
||||
|
||||
## Quick start
|
||||
|
||||
1. Rename `config.ini.example` to `config.ini` and set `DB_ROOT` to the path of the folder where your Calibre library (metadata.db) lives
|
||||
2. Execute the command: `python cps.py`
|
||||
3. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog
|
||||
1. Execute the command: `python cps.py`
|
||||
2. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog
|
||||
3. Set `Location of Calibre database` to the path of the folder where your Calibre library (metadata.db) lives, push "submit" button
|
||||
4. Go to Login page
|
||||
|
||||
**Default admin login:**
|
||||
*Username:* admin
|
||||
|
@ -36,12 +39,19 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
|||
|
||||
## Runtime Configuration Options
|
||||
|
||||
`PUBLIC_REG`
|
||||
Set to 1 to enable public user registration.
|
||||
`ANON_BROWSE`
|
||||
Set to 1 to allow not logged in users to browse the catalog.
|
||||
`UPLOADING`
|
||||
Set to 1 to enable PDF uploading. This requires the imagemagick library to be installed.
|
||||
The configuration can be changed as admin in the admin panel under "Configuration"
|
||||
|
||||
Server Port:
|
||||
Changes the port calibre-web is listening, changes take effect after pressing submit button
|
||||
|
||||
Enable public registration:
|
||||
Tick to enable public user registration.
|
||||
|
||||
Enable anonymous browsing:
|
||||
Tick to allow not logged in users to browse the catalog, anonymous user permissions can be set as admin ("Guest" user)
|
||||
|
||||
Enable uploading:
|
||||
Tick to enable uploading of PDF, epub, FB2. This requires the imagemagick library to be installed.
|
||||
|
||||
## Requirements
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user