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
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from threading import Thread
|
|
||||||
from multiprocessing import Queue
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
# Insert local directories into path
|
# Insert local directories into path
|
||||||
sys.path.insert(0, os.path.join(base_path, 'vendor'))
|
sys.path.insert(0, os.path.join(base_path, 'vendor'))
|
||||||
|
|
||||||
from cps import web
|
from cps import web
|
||||||
from cps import config
|
|
||||||
from tornado.wsgi import WSGIContainer
|
from tornado.wsgi import WSGIContainer
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
|
|
||||||
global title_sort
|
if __name__ == '__main__':
|
||||||
|
if web.ub.DEVELOPMENT:
|
||||||
|
web.app.run(host="0.0.0.0", port=web.ub.config.config_port, debug=True)
|
||||||
def start_calibreweb(messagequeue):
|
|
||||||
web.global_queue = messagequeue
|
|
||||||
if config.DEVELOPMENT:
|
|
||||||
web.app.run(host="0.0.0.0", port=config.PORT, debug=True)
|
|
||||||
else:
|
else:
|
||||||
http_server = HTTPServer(WSGIContainer(web.app))
|
http_server = HTTPServer(WSGIContainer(web.app))
|
||||||
http_server.listen(config.PORT)
|
http_server.listen(web.ub.config.config_port)
|
||||||
IOLoop.instance().start()
|
IOLoop.instance().start()
|
||||||
print "Tornado finished"
|
|
||||||
http_server.stop()
|
|
||||||
|
|
||||||
|
if web.global_task == 0:
|
||||||
def stop_calibreweb():
|
print("Performing restart of Calibre-web")
|
||||||
# Close Database connections for user and data
|
os.execl(sys.executable, sys.executable, *sys.argv)
|
||||||
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)
|
|
||||||
else:
|
else:
|
||||||
while True:
|
print("Performing shutdown of Calibre-web")
|
||||||
q = Queue()
|
sys.exit(0)
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import uploader
|
import uploader
|
||||||
import os
|
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)
|
|
123
cps/db.py
123
cps/db.py
|
@ -5,15 +5,24 @@ from sqlalchemy import *
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import *
|
from sqlalchemy.orm import *
|
||||||
import os
|
import os
|
||||||
import config
|
|
||||||
import re
|
import re
|
||||||
import ast
|
import ast
|
||||||
|
from ub import config
|
||||||
|
import ub
|
||||||
|
|
||||||
# calibre sort stuff
|
session = None
|
||||||
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
|
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):
|
def title_sort(title):
|
||||||
|
# calibre sort stuff
|
||||||
|
# config=Config()
|
||||||
|
title_pat = re.compile(config.config_title_regex, re.IGNORECASE)
|
||||||
match = title_pat.search(title)
|
match = title_pat.search(title)
|
||||||
if match:
|
if match:
|
||||||
prep = match.group(1)
|
prep = match.group(1)
|
||||||
|
@ -21,10 +30,6 @@ def title_sort(title):
|
||||||
return title.strip()
|
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()
|
Base = declarative_base()
|
||||||
|
|
||||||
books_authors_link = Table('books_authors_link', Base.metadata,
|
books_authors_link = Table('books_authors_link', Base.metadata,
|
||||||
|
@ -52,29 +57,6 @@ books_languages_link = Table('books_languages_link', Base.metadata,
|
||||||
Column('lang_code', Integer, ForeignKey('languages.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)
|
|
||||||
|
|
||||||
|
|
||||||
class Identifiers(Base):
|
class Identifiers(Base):
|
||||||
__tablename__ = 'identifiers'
|
__tablename__ = 'identifiers'
|
||||||
|
@ -245,7 +227,8 @@ class Books(Base):
|
||||||
languages = relationship('Languages', secondary=books_languages_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.title = title
|
||||||
self.sort = sort
|
self.sort = sort
|
||||||
self.author_sort = author_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,
|
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.timestamp, self.pubdate, self.series_index,
|
||||||
self.last_modified, self.path, self.has_cover)
|
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):
|
class Custom_Columns(Base):
|
||||||
|
@ -288,7 +262,76 @@ class Custom_Columns(Base):
|
||||||
display_dict = ast.literal_eval(self.display)
|
display_dict = ast.literal_eval(self.display)
|
||||||
return display_dict
|
return display_dict
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
# Base.metadata.create_all(engine)
|
||||||
Session = sessionmaker()
|
Session = sessionmaker()
|
||||||
Session.configure(bind=engine)
|
Session.configure(bind=engine)
|
||||||
session = Session()
|
session = Session()
|
||||||
|
return True
|
|
@ -1,3 +1,6 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import zipfile
|
import zipfile
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import os
|
import os
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
import os
|
import os
|
||||||
import uploader
|
import uploader
|
||||||
|
|
||||||
|
# ToDo: Check usage of original_file_name
|
||||||
def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
|
def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
|
||||||
|
|
||||||
ns = {
|
ns = {
|
||||||
|
|
227
cps/helper.py
227
cps/helper.py
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import db, ub
|
import db
|
||||||
import config
|
import ub
|
||||||
from flask import current_app as app
|
from flask import current_app as app
|
||||||
import logging
|
import logging
|
||||||
import smtplib
|
import smtplib
|
||||||
|
@ -21,7 +21,7 @@ from email.MIMEText import MIMEText
|
||||||
from email.generator import Generator
|
from email.generator import Generator
|
||||||
from flask_babel import gettext as _
|
from flask_babel import gettext as _
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
def update_download(book_id, user_id):
|
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 ==
|
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()
|
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":
|
if sys.platform == "win32":
|
||||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
|
kindlegen = os.path.join(vendorpath, u"kindlegen.exe")
|
||||||
else:
|
else:
|
||||||
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
|
kindlegen = os.path.join(vendorpath, u"kindlegen")
|
||||||
if not os.path.exists(kindlegen):
|
if not os.path.exists(kindlegen):
|
||||||
app.logger.error("make_mobi: kindlegen binary not found in: %s" % kindlegen)
|
app.logger.error("make_mobi: kindlegen binary not found in: %s" % kindlegen)
|
||||||
return None
|
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)
|
app.logger.error("make_mobi: epub format not found for book id: %d" % book_id)
|
||||||
return None
|
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"):
|
if os.path.exists(file_path + u".epub"):
|
||||||
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
|
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
|
||||||
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
||||||
|
@ -80,6 +82,7 @@ def make_mobi(book_id):
|
||||||
class StderrLogger(object):
|
class StderrLogger(object):
|
||||||
|
|
||||||
buffer = ''
|
buffer = ''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.logger = logging.getLogger('cps.web')
|
self.logger = logging.getLogger('cps.web')
|
||||||
|
|
||||||
|
@ -88,17 +91,16 @@ class StderrLogger(object):
|
||||||
self.logger.debug(self.buffer)
|
self.logger.debug(self.buffer)
|
||||||
self.buffer = ''
|
self.buffer = ''
|
||||||
else:
|
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()
|
settings = ub.get_mail_settings()
|
||||||
msg = MIMEMultipart()
|
|
||||||
msg['From'] = settings["mail_from"]
|
msg['From'] = settings["mail_from"]
|
||||||
msg['To'] = kindle_mail
|
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
|
# convert MIME message to string
|
||||||
fp = StringIO()
|
fp = StringIO()
|
||||||
|
@ -113,16 +115,14 @@ def send_test_mail(kindle_mail):
|
||||||
org_stderr = smtplib.stderr
|
org_stderr = smtplib.stderr
|
||||||
smtplib.stderr = StderrLogger()
|
smtplib.stderr = StderrLogger()
|
||||||
|
|
||||||
if int(use_ssl) == 2:
|
if use_ssl == 2:
|
||||||
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
mailserver = smtplib.SMTP_SSL(settings["mail_server"], settings["mail_port"], timeout)
|
||||||
else:
|
else:
|
||||||
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"], timeout)
|
||||||
mailserver.set_debuglevel(1)
|
mailserver.set_debuglevel(1)
|
||||||
|
|
||||||
if int(use_ssl) == 1:
|
if use_ssl == 1:
|
||||||
#mailserver.ehlo()
|
|
||||||
mailserver.starttls()
|
mailserver.starttls()
|
||||||
#mailserver.ehlo()
|
|
||||||
|
|
||||||
if settings["mail_password"]:
|
if settings["mail_password"]:
|
||||||
mailserver.login(settings["mail_login"], settings["mail_password"])
|
mailserver.login(settings["mail_login"], settings["mail_password"])
|
||||||
|
@ -138,28 +138,22 @@ def send_test_mail(kindle_mail):
|
||||||
return None
|
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"""
|
"""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
|
# create MIME message
|
||||||
msg = MIMEMultipart()
|
msg = MIMEMultipart()
|
||||||
msg['From'] = settings["mail_from"]
|
|
||||||
msg['To'] = kindle_mail
|
|
||||||
msg['Subject'] = _(u'Send to Kindle')
|
msg['Subject'] = _(u'Send to Kindle')
|
||||||
text = _(u'This email has been sent via calibre web.')
|
text = _(u'This email has been sent via calibre web.')
|
||||||
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))
|
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()
|
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)
|
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:
|
for entry in data:
|
||||||
if entry.format == "MOBI":
|
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":
|
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":
|
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:
|
if len(formats) == 0:
|
||||||
return _("Could not find any formats suitable for sending by email")
|
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:
|
if 'mobi' in formats:
|
||||||
msg.attach(get_attachment(formats['mobi']))
|
msg.attach(get_attachment(formats['mobi']))
|
||||||
elif 'epub' in formats:
|
elif 'epub' in formats:
|
||||||
filepath = make_mobi(book.id)
|
filepath = make_mobi(book.id, calibrepath)
|
||||||
if filepath is not None:
|
if filepath is not None:
|
||||||
msg.attach(get_attachment(filepath))
|
msg.attach(get_attachment(filepath))
|
||||||
elif filepath is None:
|
elif filepath is None:
|
||||||
|
@ -191,40 +185,7 @@ def send_mail(book_id, kindle_mail):
|
||||||
else:
|
else:
|
||||||
return _("Could not find any formats suitable for sending by email")
|
return _("Could not find any formats suitable for sending by email")
|
||||||
|
|
||||||
# convert MIME message to string
|
return send_raw_email(kindle_mail, msg)
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def get_attachment(file_path):
|
def get_attachment(file_path):
|
||||||
|
@ -242,8 +203,7 @@ def get_attachment(file_path):
|
||||||
return attachment
|
return attachment
|
||||||
except IOError:
|
except IOError:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
message = (_('The requested file could not be read. Maybe wrong '\
|
message = (_('The requested file could not be read. Maybe wrong permissions?')) # ToDo: What is this?
|
||||||
'permissions?'))
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,7 +213,7 @@ def get_valid_filename(value, replace_whitespace=True):
|
||||||
filename. Limits num characters to 128 max.
|
filename. Limits num characters to 128 max.
|
||||||
"""
|
"""
|
||||||
value = value[:128]
|
value = value[:128]
|
||||||
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
# re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
||||||
value = unicodedata.normalize('NFKD', value)
|
value = unicodedata.normalize('NFKD', value)
|
||||||
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
||||||
value = unicode(re_slugify.sub('', value).strip())
|
value = unicode(re_slugify.sub('', value).strip())
|
||||||
|
@ -273,10 +233,10 @@ def get_normalized_author(value):
|
||||||
return 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)
|
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()
|
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]
|
authordir = book.path.split(os.sep)[0]
|
||||||
new_authordir = get_valid_filename(book.authors[0].name, False)
|
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
|
book.path = book.path.split(os.sep)[0] + os.sep + new_titledir
|
||||||
|
|
||||||
if authordir != new_authordir:
|
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)
|
os.renames(path, new_author_path)
|
||||||
book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
|
book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
|
||||||
db.session.commit()
|
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>
|
<th>{{_('Passwd')}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for user in content %}
|
{% for user in content %}
|
||||||
{% if not user.role_anonymous() or config.ANON_BROWSE %}
|
{% if not user.role_anonymous() or config.config_anonbrowse %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{{url_for('edit_user', user_id=user.id)}}">{{user.nickname}}</a></td>
|
<td><a href="{{url_for('edit_user', user_id=user.id)}}">{{user.nickname}}</a></td>
|
||||||
<td>{{user.email}}</td>
|
<td>{{user.email}}</td>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
<h2>{{_('Configuration')}}</h2>
|
<h2>{{_('Configuration')}}</h2>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<tr>
|
<tr>
|
||||||
<th>{{_('Log File')}}</th>
|
<th>{{_('Calibre DB dir')}}</th>
|
||||||
<th>{{_('Log Level')}}</th>
|
<th>{{_('Log Level')}}</th>
|
||||||
<th>{{_('Port')}}</th>
|
<th>{{_('Port')}}</th>
|
||||||
<th>{{_('Books per page')}}</th>
|
<th>{{_('Books per page')}}</th>
|
||||||
|
@ -65,17 +65,90 @@
|
||||||
<th>{{_('Anonymous browsing')}}</th>
|
<th>{{_('Anonymous browsing')}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{config.LOG_DIR}}</td>
|
<td>{{config.config_calibre_dir}}</td>
|
||||||
<td>{{config.LOG_DIR}}</td>
|
<td>{{config.get_Log_Level()}}</td>
|
||||||
<td>{{config.PORT}}</td>
|
<td>{{config.config_port}}</td>
|
||||||
<td>{{config.NEWEST_BOOKS}}</td>
|
<td>{{config.config_books_per_page}}</td>
|
||||||
<td>{% if config.UPLOADING %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
<td>{% if config.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.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>{% if config.config_anonbrowse %}<span class="glyphicon glyphicon-ok"></span>{% else %}<span class="glyphicon glyphicon-remove"></span>{% endif %}</td>
|
||||||
</table>
|
</table>
|
||||||
|
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div>
|
||||||
<h2>{{_('Administration')}}</h2>
|
<h2>{{_('Administration')}}</h2>
|
||||||
{% if not config.DEVELOPMENT %}
|
{% if not development %}
|
||||||
<div class="btn btn-default"><a href="{{url_for('shutdown')}}">{{_('Restart Calibre-web')}}</a></div>
|
<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 %}
|
{% endif %}
|
||||||
</div>
|
</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 %}
|
{% 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>
|
<h2>{{entry.title}}</h2>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
<a href="{{url_for('author', id=author.id ) }}">{{author.name}}</a>
|
||||||
{% if not loop.last %}
|
{% if not loop.last %}
|
||||||
&
|
&
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -37,7 +37,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.series|length > 0 %}
|
{% 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 %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.languages.__len__() > 0 %}
|
{% if entry.languages.__len__() > 0 %}
|
||||||
|
@ -58,20 +58,21 @@
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if entry.tags|length > 0 %}
|
{% if entry.tags|length > 0 %}
|
||||||
<p>
|
<p>
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span class="glyphicon glyphicon-tags"></span>
|
<span class="glyphicon glyphicon-tags"></span>
|
||||||
|
|
||||||
{% for tag in entry.tags %}
|
{% 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%}
|
{%endfor%}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if entry.pubdate != '0101-01-01 00:00:00' %}
|
||||||
|
<p>{{_('Publishing date')}}: {{entry.pubdate[:10]}} </p>
|
||||||
|
{% endif %}
|
||||||
{% if cc|length > 0 %}
|
{% if cc|length > 0 %}
|
||||||
<p>
|
<p>
|
||||||
<div class="custom_columns">
|
<div class="custom_columns">
|
||||||
|
@ -101,7 +102,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if entry.comments|length > 0 %}
|
{% if entry.comments|length > 0 and entry.comments[0].text|length > 0%}
|
||||||
<h3>{{_('Description:')}}</h3>
|
<h3>{{_('Description:')}}</h3>
|
||||||
{{entry.comments[0].text|safe}}
|
{{entry.comments[0].text|safe}}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -191,7 +192,6 @@
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<div class="btn-group" role="group" aria-label="Edit/Delete book">
|
<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-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>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{% if entry.has_cover is defined %}
|
{% if entry.has_cover is defined %}
|
||||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<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 %}
|
{% if entry.ratings.__len__() > 0 %}
|
||||||
<div class="rating">
|
<div class="rating">
|
||||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||||
|
|
|
@ -29,9 +29,9 @@
|
||||||
<link rel="search"
|
<link rel="search"
|
||||||
href="{{url_for('feed_osd')}}"
|
href="{{url_for('feed_osd')}}"
|
||||||
type="application/opensearchdescription+xml"/>
|
type="application/opensearchdescription+xml"/>
|
||||||
<title>Calibre Web</title>
|
<title>{{instance}}</title>
|
||||||
<author>
|
<author>
|
||||||
<name>Calibre Web</name>
|
<name>{{instance}}</name>
|
||||||
<uri>https://github.com/janeczku/calibre-web</uri>
|
<uri>https://github.com/janeczku/calibre-web</uri>
|
||||||
</author>
|
</author>
|
||||||
|
|
||||||
|
@ -60,28 +60,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</entry>
|
</entry>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for author in authors %}
|
{% for entry in listelements %}
|
||||||
<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 %}
|
|
||||||
<entry>
|
<entry>
|
||||||
<title>{{entry.name}}</title>
|
<title>{{entry.name}}</title>
|
||||||
<id>{{ url_for('feed_category', id=entry.id) }}</id>
|
<id>{{ url_for(folder, 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;profile=opds-catalog;type=feed;kind=acquisition" href="{{url_for(folder, id=entry.id)}}"/>
|
||||||
<link type="application/atom+xml" href="{{url_for('feed_category', id=entry.id)}}" rel="subsection"/>
|
<link type="application/atom+xml" href="{{url_for(folder, 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"/>
|
|
||||||
</entry>
|
</entry>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</feed>
|
</feed>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{% if g.user.show_random_books() %}
|
{% if g.user.show_detail_random() %}
|
||||||
<div class="discover">
|
<div class="discover">
|
||||||
<h2>{{_('Discover (Random Books)')}}</h2>
|
<h2>{{_('Discover (Random Books)')}}</h2>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<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 %}
|
{% if entry.ratings.__len__() > 0 %}
|
||||||
<div class="rating">
|
<div class="rating">
|
||||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<p class="title">{{entry.title|shortentitle}}</p>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
<a href="{{url_for('author', id=author.id) }}">{{author.name}}</a>
|
||||||
{% if not loop.last %}
|
{% if not loop.last %}
|
||||||
&
|
&
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
type="application/atom+xml;profile=opds-catalog;kind=navigation"/>
|
||||||
<link rel="search" title="{{_('Search')}}" href="{{url_for('feed_osd')}}"
|
<link rel="search" title="{{_('Search')}}" href="{{url_for('feed_osd')}}"
|
||||||
type="application/opensearchdescription+xml"/>
|
type="application/opensearchdescription+xml"/>
|
||||||
<title>Calibre Web</title>
|
<title>{{instance}}</title>
|
||||||
<author>
|
<author>
|
||||||
<name>Calibre Web</name>
|
<name>{{instance}}</name>
|
||||||
<uri>https://github.com/janeczku/calibre-web</uri>
|
<uri>https://github.com/janeczku/calibre-web</uri>
|
||||||
</author>
|
</author>
|
||||||
<entry>
|
<entry>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>calibre web | {{title}}</title>
|
<title>{{instance}} | {{title}}</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div>
|
<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>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
|
||||||
<LongName>Calibre Web</LongName>
|
<LongName>{{instance}}</LongName>
|
||||||
<ShortName>Calibre Web</ShortName>
|
<ShortName>{{instance}}</ShortName>
|
||||||
<Description>Calibre Web ebook catalog</Description>
|
<Description>{{_('instanceCalibre Web ebook catalog')}}</Description>
|
||||||
<Developer>janeczku</Developer>
|
<Developer>Janeczku</Developer>
|
||||||
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
<Contact>https://github.com/janeczku/calibre-web</Contact>
|
||||||
<Url type="text/html"
|
<Url type="text/html"
|
||||||
template="{{url_for('search')}}?query={searchTerms}"/>
|
template="{{url_for('search')}}?query={searchTerms}"/>
|
||||||
<Url type="application/atom+xml"
|
<Url type="application/atom+xml"
|
||||||
template="{{url_for('feed_normal_search')}}?query={searchTerms}"/>
|
template="{{url_for('feed_normal_search')}}?query={searchTerms}"/>
|
||||||
<SyndicationRight>open</SyndicationRight>
|
<SyndicationRight>open</SyndicationRight>
|
||||||
<Language>de-DE</Language>
|
<Language>{{lang}}</Language>
|
||||||
<OutputEncoding>UTF-8</OutputEncoding>
|
<OutputEncoding>UTF-8</OutputEncoding>
|
||||||
<InputEncoding>UTF-8</InputEncoding>
|
<InputEncoding>UTF-8</InputEncoding>
|
||||||
</OpenSearchDescription>
|
</OpenSearchDescription>
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
<!-- Full Screen -->
|
<!-- Full Screen -->
|
||||||
<!--<script src="js/libs/screenfull.min.js"></script>-->
|
<!--<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 -->
|
<!-- Render -->
|
||||||
<!--<script src="js/epub.min.js"></script>-->
|
<!--<script src="js/epub.min.js"></script>-->
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="cover">
|
<div class="cover">
|
||||||
{% if entry.has_cover is defined %}
|
{% if entry.has_cover is defined %}
|
||||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<p class="title">{{entry.title|shortentitle}}</p>
|
||||||
<p class="author">
|
<p class="author">
|
||||||
{% for author in entry.authors %}
|
{% for author in entry.authors %}
|
||||||
<a href="{{url_for('author', name=author.name) }}">{{author.name}}</a>
|
<a href="{{url_for('author', name=author.name | urlencode) }}">{{author.name}}</a>
|
||||||
{% if not loop.last %}
|
{% if not loop.last %}
|
||||||
&
|
&
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -44,7 +44,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="cover">
|
||||||
{% if entry.has_cover is defined %}
|
{% if entry.has_cover is defined %}
|
||||||
<a href="{{ url_for('show_book', id=entry.id) }}">
|
<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>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<p class="title">{{entry.title|shortentitle}}</p>
|
<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 %}
|
{% if entry.ratings.__len__() > 0 %}
|
||||||
<div class="rating">
|
<div class="rating">
|
||||||
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
|
||||||
|
@ -36,7 +36,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- <p><a href="{{ url_for('edit_book', book_id=entry.id) }}">{{entry.authors[0].name}}: {{entry.title}}</a></p> -->
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,19 +12,19 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Python</th>
|
<th>Python</th>
|
||||||
<td>{{Versions['PythonVersion']}}</td>
|
<td>{{versions['PythonVersion']}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Kindlegen</th>
|
<th>Kindlegen</th>
|
||||||
<td>{{Versions['KindlegenVersion']}}</td>
|
<td>{{versions['KindlegenVersion']}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ImageMagick</th>
|
<th>ImageMagick</th>
|
||||||
<td>{{Versions['ImageVersion']}}</td>
|
<td>{{versions['ImageVersion']}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>PyPDF2</th>
|
<th>PyPDF2</th>
|
||||||
<td>{{Versions['PyPdfVersion']}}</td>
|
<td>{{versions['PyPdfVersion']}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -40,6 +40,14 @@
|
||||||
<th>{{authorcounter}}</th>
|
<th>{{authorcounter}}</th>
|
||||||
<td>{{_('Authors in this Library')}}</td>
|
<td>{{_('Authors in this Library')}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{categorycounter}}</th>
|
||||||
|
<td>{{_('Categories in this Library')}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{{seriecounter}}</th>
|
||||||
|
<td>{{_('Series in this Library')}}</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -27,39 +27,47 @@
|
||||||
<label for="locale">{{_('Language')}}</label>
|
<label for="locale">{{_('Language')}}</label>
|
||||||
<select name="locale" id="locale" class="form-control">
|
<select name="locale" id="locale" class="form-control">
|
||||||
{% for translation in translations %}
|
{% 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 %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default_language">{{_('Show books with language')}}</label>
|
<label for="default_language">{{_('Show books with language')}}</label>
|
||||||
<select name="default_language" id="default_language" class="form-control">
|
<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 %}
|
{% for language in languages %}
|
||||||
<option value="{{ language.lang_code }}" {% if content.default_language == language.lang_code %}selected{% endif %}>{{ language.name }}</option>
|
<option value="{{ language.lang_code }}" {% if content.default_language == language.lang_code %}selected{% endif %}>{{ language.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<label for="show_random">{{_('Show random books')}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<label for="show_hot">{{_('Show hot books')}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<label for="show_language">{{_('Show language selection')}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<label for="show_series">{{_('Show series selection')}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<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>
|
<label for="show_category">{{_('Show category selection')}}</label>
|
||||||
</div>
|
</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 g.user and g.user.role_admin() and not profile %}
|
||||||
{% if not content.role_anonymous() %}
|
{% if not content.role_anonymous() %}
|
||||||
|
@ -105,7 +113,7 @@
|
||||||
{% for entry in downloads %}
|
{% for entry in downloads %}
|
||||||
<div class="col-sm-2">
|
<div class="col-sm-2">
|
||||||
<a class="pull-left" href="{{ url_for('show_book', id=entry.id) }}">
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% 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 ""
|
|
||||||
|
|
209
cps/ub.py
209
cps/ub.py
|
@ -7,12 +7,12 @@ from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.orm import *
|
from sqlalchemy.orm import *
|
||||||
from flask_login import AnonymousUserMixin
|
from flask_login import AnonymousUserMixin
|
||||||
import os
|
import os
|
||||||
import config
|
|
||||||
import traceback
|
import traceback
|
||||||
|
import logging
|
||||||
from werkzeug.security import generate_password_hash
|
from werkzeug.security import generate_password_hash
|
||||||
from flask_babel import gettext as _
|
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)
|
engine = create_engine('sqlite:///{0}'.format(dbpath), echo=False)
|
||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
|
|
||||||
|
@ -23,10 +23,26 @@ ROLE_UPLOAD = 4
|
||||||
ROLE_EDIT = 8
|
ROLE_EDIT = 8
|
||||||
ROLE_PASSWD = 16
|
ROLE_PASSWD = 16
|
||||||
ROLE_ANONYMOUS = 32
|
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"
|
DEFAULT_PASS = "admin123"
|
||||||
|
|
||||||
|
|
||||||
class UserBase():
|
|
||||||
|
DEVELOPMENT = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UserBase:
|
||||||
|
@staticmethod
|
||||||
def is_authenticated(self):
|
def is_authenticated(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -79,24 +95,54 @@ class UserBase():
|
||||||
return self.default_language
|
return self.default_language
|
||||||
|
|
||||||
def show_random_books(self):
|
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):
|
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):
|
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):
|
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):
|
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):
|
def __repr__(self):
|
||||||
return '<User %r>' % self.nickname
|
return '<User %r>' % self.nickname
|
||||||
|
|
||||||
|
|
||||||
|
# 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):
|
class User(UserBase, Base):
|
||||||
__tablename__ = 'user'
|
__tablename__ = 'user'
|
||||||
|
|
||||||
|
@ -109,30 +155,34 @@ class User(UserBase,Base):
|
||||||
shelf = relationship('Shelf', backref='user', lazy='dynamic')
|
shelf = relationship('Shelf', backref='user', lazy='dynamic')
|
||||||
downloads = relationship('Downloads', backref='user', lazy='dynamic')
|
downloads = relationship('Downloads', backref='user', lazy='dynamic')
|
||||||
locale = Column(String(2), default="en")
|
locale = Column(String(2), default="en")
|
||||||
random_books = Column(Integer, default=1)
|
sidebar_view = Column(Integer, default=1)
|
||||||
language_books = Column(Integer, default=1)
|
#language_books = Column(Integer, default=1)
|
||||||
series_books = Column(Integer, default=1)
|
#series_books = Column(Integer, default=1)
|
||||||
category_books = Column(Integer, default=1)
|
#category_books = Column(Integer, default=1)
|
||||||
hot_books = Column(Integer, default=1)
|
#hot_books = Column(Integer, default=1)
|
||||||
default_language = Column(String(3), default="all")
|
default_language = Column(String(3), default="all")
|
||||||
|
|
||||||
|
|
||||||
|
# Class for anonymous user is derived from User base and complets overrides methods and properties for the
|
||||||
|
# anonymous user
|
||||||
class Anonymous(AnonymousUserMixin, UserBase):
|
class Anonymous(AnonymousUserMixin, UserBase):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.loadSettings()
|
self.loadSettings()
|
||||||
|
|
||||||
def loadSettings(self):
|
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.nickname = data.nickname
|
||||||
self.role = data.role
|
self.role = data.role
|
||||||
self.random_books = data.random_books
|
self.sidebar_view = data.sidebar_view
|
||||||
self.default_language = data.default_language
|
self.default_language = data.default_language
|
||||||
self.language_books = data.language_books
|
#self.language_books = data.language_books
|
||||||
self.series_books = data.series_books
|
#self.series_books = data.series_books
|
||||||
self.category_books = data.category_books
|
#self.category_books = data.category_books
|
||||||
self.hot_books = data.hot_books
|
#self.hot_books = data.hot_books
|
||||||
self.default_language = data.default_language
|
self.default_language = data.default_language
|
||||||
self.locale = data.locale
|
self.locale = data.locale
|
||||||
|
self.anon_browse = settings.config_anonbrowse
|
||||||
|
|
||||||
def role_admin(self):
|
def role_admin(self):
|
||||||
return False
|
return False
|
||||||
|
@ -141,9 +191,10 @@ class Anonymous(AnonymousUserMixin,UserBase):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_anonymous(self):
|
def is_anonymous(self):
|
||||||
return config.ANON_BROWSE
|
return self.anon_browse
|
||||||
|
|
||||||
|
|
||||||
|
# Baseclass representing Shelfs in calibre-web inapp.db
|
||||||
class Shelf(Base):
|
class Shelf(Base):
|
||||||
__tablename__ = 'shelf'
|
__tablename__ = 'shelf'
|
||||||
|
|
||||||
|
@ -155,6 +206,8 @@ class Shelf(Base):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Shelf %r>' % self.name
|
return '<Shelf %r>' % self.name
|
||||||
|
|
||||||
|
|
||||||
|
# Baseclass representing Relationship between books and Shelfs in Calibre-web in app.db (N:M)
|
||||||
class BookShelf(Base):
|
class BookShelf(Base):
|
||||||
__tablename__ = 'book_shelf_link'
|
__tablename__ = 'book_shelf_link'
|
||||||
|
|
||||||
|
@ -167,6 +220,7 @@ class BookShelf(Base):
|
||||||
return '<Book %r>' % self.id
|
return '<Book %r>' % self.id
|
||||||
|
|
||||||
|
|
||||||
|
# Baseclass representing Downloads from calibre-web in app.db
|
||||||
class Downloads(Base):
|
class Downloads(Base):
|
||||||
__tablename__ = 'downloads'
|
__tablename__ = 'downloads'
|
||||||
|
|
||||||
|
@ -177,6 +231,9 @@ class Downloads(Base):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Download %r' % self.book_id
|
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):
|
class Settings(Base):
|
||||||
__tablename__ = 'settings'
|
__tablename__ = 'settings'
|
||||||
|
|
||||||
|
@ -187,42 +244,120 @@ class Settings(Base):
|
||||||
mail_login = Column(String)
|
mail_login = Column(String)
|
||||||
mail_password = Column(String)
|
mail_password = Column(String)
|
||||||
mail_from = 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):
|
def __repr__(self):
|
||||||
#return '<Smtp %r>' % (self.mail_server)
|
|
||||||
pass
|
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():
|
def migrate_Database():
|
||||||
if session.query(User).filter(User.role.op('&')(ROLE_ANONYMOUS) == ROLE_ANONYMOUS).first() is None:
|
|
||||||
create_anonymous_user()
|
|
||||||
try:
|
try:
|
||||||
session.query(exists().where(User.random_books)).scalar()
|
session.query(exists().where(User.locale)).scalar()
|
||||||
session.commit()
|
session.commit()
|
||||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||||
conn = engine.connect()
|
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 locale String(2) DEFAULT 'en'")
|
||||||
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
|
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
|
||||||
session.commit()
|
session.commit()
|
||||||
try:
|
try:
|
||||||
session.query(exists().where(User.language_books)).scalar()
|
session.query(exists().where(Settings.config_calibre_dir)).scalar()
|
||||||
session.commit()
|
session.commit()
|
||||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||||
conn = engine.connect()
|
conn = engine.connect()
|
||||||
conn.execute("ALTER TABLE user ADD column language_books INTEGER DEFAULT 1")
|
conn.execute("ALTER TABLE Settings ADD column `config_calibre_dir` String")
|
||||||
conn.execute("ALTER TABLE user ADD column series_books INTEGER DEFAULT 1")
|
conn.execute("ALTER TABLE Settings ADD column `config_port` INTEGER DEFAULT 8083")
|
||||||
conn.execute("ALTER TABLE user ADD column category_books INTEGER DEFAULT 1")
|
conn.execute("ALTER TABLE Settings ADD column `config_calibre_web_title` String DEFAULT 'Calibre-web'")
|
||||||
conn.execute("ALTER TABLE user ADD column hot_books INTEGER DEFAULT 1")
|
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()
|
session.commit()
|
||||||
try:
|
try:
|
||||||
session.query(exists().where(BookShelf.order)).scalar()
|
session.query(exists().where(BookShelf.order)).scalar()
|
||||||
session.commit()
|
session.commit()
|
||||||
except exc.OperationalError: # Database is not compatible, some rows are missing
|
except exc.OperationalError: # Database is not compatible, some rows are missing
|
||||||
conn = engine.connect()
|
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()
|
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():
|
def create_default_config():
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
@ -254,6 +389,8 @@ def get_mail_settings():
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
# Generate user Guest (translated text), as anoymous user, no rights
|
||||||
def create_anonymous_user():
|
def create_anonymous_user():
|
||||||
user = User()
|
user = User()
|
||||||
user.nickname = _("Guest")
|
user.nickname = _("Guest")
|
||||||
|
@ -269,10 +406,14 @@ def create_anonymous_user():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Generate User admin with admin123 password, and access to everything
|
||||||
def create_admin_user():
|
def create_admin_user():
|
||||||
user = User()
|
user = User()
|
||||||
user.nickname = "admin"
|
user.nickname = "admin"
|
||||||
user.role = ROLE_USER + ROLE_ADMIN + ROLE_DOWNLOAD + ROLE_UPLOAD + ROLE_EDIT + ROLE_PASSWD
|
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)
|
user.password = generate_password_hash(DEFAULT_PASS)
|
||||||
|
|
||||||
session.add(user)
|
session.add(user)
|
||||||
|
@ -282,10 +423,13 @@ def create_admin_user():
|
||||||
session.rollback()
|
session.rollback()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Open session for database connection
|
||||||
Session = sessionmaker()
|
Session = sessionmaker()
|
||||||
Session.configure(bind=engine)
|
Session.configure(bind=engine)
|
||||||
session = Session()
|
session = Session()
|
||||||
|
|
||||||
|
# generate database and admin and guest user, if no database is existing
|
||||||
if not os.path.exists(dbpath):
|
if not os.path.exists(dbpath):
|
||||||
try:
|
try:
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
|
@ -296,3 +440,6 @@ if not os.path.exists(dbpath):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
migrate_Database()
|
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
|
import os
|
||||||
from tempfile import gettempdir
|
from tempfile import gettempdir
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
733
cps/web.py
733
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 ""
|
msgstr ""
|
||||||
"Project-Id-Version: PROJECT VERSION\n"
|
"Project-Id-Version: PROJECT VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2017-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"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -17,284 +17,308 @@ msgstr ""
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.3.4\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"
|
msgid "not installed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/helper.py:98
|
#: cps/helper.py:136
|
||||||
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
|
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to send mail: %s"
|
msgid "Failed to send mail: %s"
|
||||||
msgstr ""
|
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"
|
msgid "Send to Kindle"
|
||||||
msgstr ""
|
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"
|
msgid "Could not find any formats suitable for sending by email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/helper.py:186
|
#: cps/helper.py:180
|
||||||
msgid "Could not convert epub to mobi"
|
msgid "Could not convert epub to mobi"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/helper.py:245
|
#: cps/helper.py:206
|
||||||
msgid "The requested file could not be read. Maybe wrong permissions?"
|
msgid "The requested file could not be read. Maybe wrong permissions?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/ub.py:259
|
#: cps/ub.py:380
|
||||||
msgid "Guest"
|
msgid "Guest"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:742
|
#: cps/web.py:774
|
||||||
msgid "Latest Books"
|
msgid "Latest Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:767
|
#: cps/web.py:799
|
||||||
msgid "Hot Books (most downloaded)"
|
msgid "Hot Books (most downloaded)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/index.xml:29 cps/web.py:775
|
#: cps/templates/index.xml:29 cps/web.py:808
|
||||||
msgid "Random Books"
|
msgid "Random Books"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:788
|
#: cps/web.py:821
|
||||||
msgid "Author list"
|
msgid "Author list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:805
|
#: cps/web.py:838
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Author: %(nam)s"
|
msgid "Author: %(nam)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/index.xml:50 cps/web.py:818
|
#: cps/templates/index.xml:50 cps/web.py:851
|
||||||
msgid "Series list"
|
msgid "Series list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:829
|
#: cps/web.py:862
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Series: %(serie)s"
|
msgid "Series: %(serie)s"
|
||||||
msgstr ""
|
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:"
|
msgid "Error opening eBook. File does not exist or file is not accessible:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:862
|
#: cps/web.py:895
|
||||||
msgid "Available languages"
|
msgid "Available languages"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:877
|
#: cps/web.py:910
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Language: %(name)s"
|
msgid "Language: %(name)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/index.xml:43 cps/web.py:890
|
#: cps/templates/index.xml:43 cps/web.py:923
|
||||||
msgid "Category list"
|
msgid "Category list"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:900
|
#: cps/web.py:933
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Category: %(name)s"
|
msgid "Category: %(name)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:956
|
#: cps/web.py:992
|
||||||
msgid "Statistics"
|
msgid "Statistics"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:965
|
#: cps/web.py:1013
|
||||||
msgid "Server restarts"
|
msgid "Performing Restart, please reload page"
|
||||||
msgstr ""
|
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"
|
msgid "Read a Book"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1172 cps/web.py:1510
|
#: cps/web.py:1227 cps/web.py:1649
|
||||||
msgid "Please fill out all fields!"
|
msgid "Please fill out all fields!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1188
|
#: cps/web.py:1228 cps/web.py:1244 cps/web.py:1249 cps/web.py:1251
|
||||||
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"
|
msgid "register"
|
||||||
msgstr ""
|
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
|
#, python-format
|
||||||
msgid "you are now logged in as: '%(nickname)s'"
|
msgid "you are now logged in as: '%(nickname)s'"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1216
|
#: cps/web.py:1270
|
||||||
msgid "Wrong Username or Password"
|
msgid "Wrong Username or Password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1218
|
#: cps/web.py:1272
|
||||||
msgid "login"
|
msgid "login"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1235
|
#: cps/web.py:1289
|
||||||
msgid "Please configure the SMTP mail settings first..."
|
msgid "Please configure the SMTP mail settings first..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1239
|
#: cps/web.py:1293
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Book successfully send to %(kindlemail)s"
|
msgid "Book successfully send to %(kindlemail)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1243
|
#: cps/web.py:1297
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "There was an error sending this book: %(res)s"
|
msgid "There was an error sending this book: %(res)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1245
|
#: cps/web.py:1299
|
||||||
msgid "Please configure your kindle email address first..."
|
msgid "Please configure your kindle email address first..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1265
|
#: cps/web.py:1319
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Book has been added to shelf: %(sname)s"
|
msgid "Book has been added to shelf: %(sname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1286
|
#: cps/web.py:1340
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Book has been removed from shelf: %(sname)s"
|
msgid "Book has been removed from shelf: %(sname)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1304 cps/web.py:1325
|
#: cps/web.py:1359 cps/web.py:1383
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "A shelf with the name '%(title)s' already exists."
|
msgid "A shelf with the name '%(title)s' already exists."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1309
|
#: cps/web.py:1364
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Shelf %(title)s created"
|
msgid "Shelf %(title)s created"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1311 cps/web.py:1336
|
#: cps/web.py:1366 cps/web.py:1394
|
||||||
msgid "There was an error"
|
msgid "There was an error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1312 cps/web.py:1314
|
#: cps/web.py:1367 cps/web.py:1369
|
||||||
msgid "create a shelf"
|
msgid "create a shelf"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1334
|
#: cps/web.py:1392
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Shelf %(title)s changed"
|
msgid "Shelf %(title)s changed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1337 cps/web.py:1339
|
#: cps/web.py:1395 cps/web.py:1397
|
||||||
msgid "Edit a shelf"
|
msgid "Edit a shelf"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1360
|
#: cps/web.py:1415
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "successfully deleted shelf %(name)s"
|
msgid "successfully deleted shelf %(name)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1381
|
#: cps/web.py:1437
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Shelf: '%(name)s'"
|
msgid "Shelf: '%(name)s'"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1409
|
#: cps/web.py:1468
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Change order of Shelf: '%(name)s'"
|
msgid "Change order of Shelf: '%(name)s'"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1469
|
#: cps/web.py:1528
|
||||||
msgid "Found an existing account for this email address."
|
msgid "Found an existing account for this email address."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1471 cps/web.py:1474
|
#: cps/web.py:1530 cps/web.py:1534
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(name)s's profile"
|
msgid "%(name)s's profile"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1472
|
#: cps/web.py:1531
|
||||||
msgid "Profile updated"
|
msgid "Profile updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1483 cps/web.py:1491
|
#: cps/web.py:1544
|
||||||
msgid "Admin page"
|
msgid "Admin page"
|
||||||
msgstr ""
|
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"
|
msgid "Add new user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1544
|
#: cps/web.py:1687
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User '%(user)s' created"
|
msgid "User '%(user)s' created"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1548
|
#: cps/web.py:1691
|
||||||
msgid "Found an existing account for this email address or nickname."
|
msgid "Found an existing account for this email address or nickname."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1568
|
#: cps/web.py:1711
|
||||||
msgid "Mail settings updated"
|
msgid "Mail settings updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1574
|
#: cps/web.py:1717
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
msgid "Test E-Mail successfully send to %(kindlemail)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1577
|
#: cps/web.py:1720
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "There was an error sending the Test E-Mail: %(res)s"
|
msgid "There was an error sending the Test E-Mail: %(res)s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1578
|
#: cps/web.py:1721
|
||||||
msgid "Edit mail settings"
|
msgid "Edit mail settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1606
|
#: cps/web.py:1749
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User '%(nick)s' deleted"
|
msgid "User '%(nick)s' deleted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1661
|
#: cps/web.py:1825
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User '%(nick)s' updated"
|
msgid "User '%(nick)s' updated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1664
|
#: cps/web.py:1828
|
||||||
msgid "An unknown error occured."
|
msgid "An unknown error occured."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1666
|
#: cps/web.py:1831
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Edit User %(nick)s"
|
msgid "Edit User %(nick)s"
|
||||||
msgstr ""
|
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
|
#, python-format
|
||||||
msgid "Failed to create path %s (Permission denied)."
|
msgid "Failed to create path %s (Permission denied)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1909
|
#: cps/web.py:2076
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to store file %s (Permission denied)."
|
msgid "Failed to store file %s (Permission denied)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/web.py:1914
|
#: cps/web.py:2081
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "Failed to delete file %s (Permission denied)."
|
msgid "Failed to delete file %s (Permission denied)."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -323,7 +347,7 @@ msgstr ""
|
||||||
msgid "Admin"
|
msgid "Admin"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:12 cps/templates/detail.html:114
|
#: cps/templates/admin.html:12 cps/templates/detail.html:116
|
||||||
msgid "Download"
|
msgid "Download"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -371,15 +395,15 @@ msgstr ""
|
||||||
msgid "Change SMTP settings"
|
msgid "Change SMTP settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:56
|
#: cps/templates/admin.html:56 cps/templates/admin.html:76
|
||||||
msgid "Configuration"
|
msgid "Configuration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:59
|
#: cps/templates/admin.html:59
|
||||||
msgid "Log File"
|
msgid "Calibre DB dir"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:60
|
#: cps/templates/admin.html:60 cps/templates/config_edit.html:32
|
||||||
msgid "Log Level"
|
msgid "Log Level"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -387,7 +411,7 @@ msgstr ""
|
||||||
msgid "Port"
|
msgid "Port"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:62
|
#: cps/templates/admin.html:62 cps/templates/config_edit.html:19
|
||||||
msgid "Books per page"
|
msgid "Books per page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -403,101 +427,155 @@ msgstr ""
|
||||||
msgid "Anonymous browsing"
|
msgid "Anonymous browsing"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:76
|
#: cps/templates/admin.html:77
|
||||||
msgid "Administration"
|
msgid "Administration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/admin.html:78
|
#: cps/templates/admin.html:79
|
||||||
msgid "Restart Calibre-web"
|
msgid "Restart Calibre-web"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:38
|
#: cps/templates/admin.html:80
|
||||||
msgid "Book"
|
msgid "Stop Calibre-web"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:38
|
#: cps/templates/admin.html:91
|
||||||
msgid "of"
|
msgid "Do you really want to restart Calibre-web?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:44
|
#: cps/templates/admin.html:92 cps/templates/admin.html:107
|
||||||
msgid "language"
|
msgid "Ok"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:103
|
#: cps/templates/admin.html:93 cps/templates/admin.html:108
|
||||||
msgid "Description:"
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:131
|
#: cps/templates/admin.html:106
|
||||||
msgid "Read in browser"
|
msgid "Do you really want to stop Calibre-web?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/detail.html:151
|
#: cps/templates/book_edit.html:16 cps/templates/search_form.html:6
|
||||||
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"
|
msgid "Book Title"
|
||||||
msgstr ""
|
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"
|
msgid "Author"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:22
|
#: cps/templates/book_edit.html:24
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr ""
|
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"
|
msgid "Tags"
|
||||||
msgstr ""
|
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
|
#: cps/templates/search_form.html:33
|
||||||
msgid "Series"
|
msgid "Series"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:35
|
#: cps/templates/book_edit.html:37
|
||||||
msgid "Series id"
|
msgid "Series id"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:39
|
#: cps/templates/book_edit.html:41
|
||||||
msgid "Rating"
|
msgid "Rating"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:43
|
#: cps/templates/book_edit.html:45
|
||||||
msgid "Cover URL (jpg)"
|
msgid "Cover URL (jpg)"
|
||||||
msgstr ""
|
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"
|
msgid "Language"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:59
|
#: cps/templates/book_edit.html:61
|
||||||
msgid "Yes"
|
msgid "Yes"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:60
|
#: cps/templates/book_edit.html:62
|
||||||
msgid "No"
|
msgid "No"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:102
|
#: cps/templates/book_edit.html:104
|
||||||
msgid "view book after edit"
|
msgid "view book after edit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:105 cps/templates/login.html:19
|
#: cps/templates/book_edit.html:107 cps/templates/config_edit.html:52
|
||||||
#: cps/templates/search_form.html:75 cps/templates/shelf_edit.html:15
|
#: cps/templates/login.html:19 cps/templates/search_form.html:75
|
||||||
#: cps/templates/user_edit.html:97
|
#: cps/templates/shelf_edit.html:15 cps/templates/user_edit.html:105
|
||||||
msgid "Submit"
|
msgid "Submit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/edit_book.html:106 cps/templates/email_edit.html:36
|
#: cps/templates/config_edit.html:7
|
||||||
#: cps/templates/shelf_edit.html:17 cps/templates/shelf_order.html:12
|
msgid "Location of Calibre database"
|
||||||
#: cps/templates/user_edit.html:99
|
msgstr ""
|
||||||
msgid "Back"
|
|
||||||
|
#: 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 ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/email_edit.html:11
|
#: cps/templates/email_edit.html:11
|
||||||
|
@ -600,10 +678,6 @@ msgstr ""
|
||||||
msgid "Logout"
|
msgid "Logout"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/layout.html:91 cps/templates/login.html:4
|
|
||||||
msgid "Login"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: cps/templates/layout.html:92 cps/templates/register.html:18
|
#: cps/templates/layout.html:92 cps/templates/register.html:18
|
||||||
msgid "Register"
|
msgid "Register"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -722,10 +796,6 @@ msgstr ""
|
||||||
msgid "Change order"
|
msgid "Change order"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/shelf_edit.html:7
|
|
||||||
msgid "Title"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: cps/templates/shelf_edit.html:12
|
#: cps/templates/shelf_edit.html:12
|
||||||
msgid "should the shelf be public?"
|
msgid "should the shelf be public?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -790,31 +860,39 @@ msgstr ""
|
||||||
msgid "Show category selection"
|
msgid "Show category selection"
|
||||||
msgstr ""
|
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"
|
msgid "Admin user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:73
|
#: cps/templates/user_edit.html:81
|
||||||
msgid "Allow Downloads"
|
msgid "Allow Downloads"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:77
|
#: cps/templates/user_edit.html:85
|
||||||
msgid "Allow Uploads"
|
msgid "Allow Uploads"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:81
|
#: cps/templates/user_edit.html:89
|
||||||
msgid "Allow Edit"
|
msgid "Allow Edit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:86
|
#: cps/templates/user_edit.html:94
|
||||||
msgid "Allow Changing Password"
|
msgid "Allow Changing Password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:93
|
#: cps/templates/user_edit.html:101
|
||||||
msgid "Delete this user"
|
msgid "Delete this user"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: cps/templates/user_edit.html:104
|
#: cps/templates/user_edit.html:112
|
||||||
msgid "Recent Downloads"
|
msgid "Recent Downloads"
|
||||||
msgstr ""
|
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
|
##Features
|
||||||
- Bootstrap 3 HTML5 interface
|
- Bootstrap 3 HTML5 interface
|
||||||
|
- full graphical setup
|
||||||
- User management
|
- User management
|
||||||
- Admin interface
|
- Admin interface
|
||||||
- User Interface in english, french, german, simplified chinese, spanish
|
- 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
|
- Upload new books in PDF, epub, fb2 format
|
||||||
- Support for Calibre custom columns
|
- Support for Calibre custom columns
|
||||||
- Fine grained per-user permissions
|
- Fine grained per-user permissions
|
||||||
|
- Self update capability
|
||||||
|
|
||||||
## Quick start
|
## 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
|
1. Execute the command: `python cps.py`
|
||||||
2. Execute the command: `python cps.py`
|
2. Point your browser to `http://localhost:8083` or `http://localhost:8083/opds` for the OPDS catalog
|
||||||
3. 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:**
|
**Default admin login:**
|
||||||
*Username:* admin
|
*Username:* admin
|
||||||
|
@ -36,12 +39,19 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
|
||||||
|
|
||||||
## Runtime Configuration Options
|
## Runtime Configuration Options
|
||||||
|
|
||||||
`PUBLIC_REG`
|
The configuration can be changed as admin in the admin panel under "Configuration"
|
||||||
Set to 1 to enable public user registration.
|
|
||||||
`ANON_BROWSE`
|
Server Port:
|
||||||
Set to 1 to allow not logged in users to browse the catalog.
|
Changes the port calibre-web is listening, changes take effect after pressing submit button
|
||||||
`UPLOADING`
|
|
||||||
Set to 1 to enable PDF uploading. This requires the imagemagick library to be installed.
|
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
|
## Requirements
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user