Commit 5dd08e43 authored by Michael Knepher's avatar Michael Knepher

Merge remote-tracking branch 'upstream/master'

parents 2120d729 2f8c8c3a
...@@ -37,6 +37,7 @@ from . import config_sql, logger, cache_buster, cli, ub, db ...@@ -37,6 +37,7 @@ from . import config_sql, logger, cache_buster, cli, ub, db
from .reverseproxy import ReverseProxied from .reverseproxy import ReverseProxied
from .server import WebServer from .server import WebServer
mimetypes.init() mimetypes.init()
mimetypes.add_type('application/xhtml+xml', '.xhtml') mimetypes.add_type('application/xhtml+xml', '.xhtml')
mimetypes.add_type('application/epub+zip', '.epub') mimetypes.add_type('application/epub+zip', '.epub')
...@@ -59,7 +60,7 @@ app = Flask(__name__) ...@@ -59,7 +60,7 @@ app = Flask(__name__)
app.config.update( app.config.update(
SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE='Lax', SESSION_COOKIE_SAMESITE='Lax',
REMEMBER_COOKIE_SAMESITE='Lax', REMEMBER_COOKIE_SAMESITE='Lax', # will be available in flask-login 0.5.1 earliest
) )
...@@ -82,6 +83,8 @@ log = logger.create() ...@@ -82,6 +83,8 @@ log = logger.create()
from . import services from . import services
calibre_db = db.CalibreDB()
def create_app(): def create_app():
app.wsgi_app = ReverseProxied(app.wsgi_app) app.wsgi_app = ReverseProxied(app.wsgi_app)
# For python2 convert path to unicode # For python2 convert path to unicode
...@@ -98,7 +101,8 @@ def create_app(): ...@@ -98,7 +101,8 @@ def create_app():
app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session)) app.secret_key = os.getenv('SECRET_KEY', config_sql.get_flask_session_key(ub.session))
web_server.init_app(app, config) web_server.init_app(app, config)
db.setup_db(config) calibre_db.setup_db(config, cli.settingspath)
calibre_db.start()
babel.init_app(app) babel.init_app(app)
_BABEL_TRANSLATIONS.update(str(item) for item in babel.list_translations()) _BABEL_TRANSLATIONS.update(str(item) for item in babel.list_translations())
......
...@@ -30,7 +30,7 @@ import babel, pytz, requests, sqlalchemy ...@@ -30,7 +30,7 @@ import babel, pytz, requests, sqlalchemy
import werkzeug, flask, flask_login, flask_principal, jinja2 import werkzeug, flask, flask_login, flask_principal, jinja2
from flask_babel import gettext as _ from flask_babel import gettext as _
from . import db, converter, uploader, server, isoLanguages, constants from . import db, calibre_db, converter, uploader, server, isoLanguages, constants
from .web import render_title_template from .web import render_title_template
try: try:
from flask_login import __version__ as flask_loginVersion from flask_login import __version__ as flask_loginVersion
...@@ -85,10 +85,12 @@ _VERSIONS.update(uploader.get_versions()) ...@@ -85,10 +85,12 @@ _VERSIONS.update(uploader.get_versions())
@about.route("/stats") @about.route("/stats")
@flask_login.login_required @flask_login.login_required
def stats(): def stats():
counter = db.session.query(db.Books).count() counter = calibre_db.session.query(db.Books).count()
authors = db.session.query(db.Authors).count() authors = calibre_db.session.query(db.Authors).count()
categorys = db.session.query(db.Tags).count() categorys = calibre_db.session.query(db.Tags).count()
series = db.session.query(db.Series).count() series = calibre_db.session.query(db.Series).count()
_VERSIONS['ebook converter'] = _(converter.get_version()) _VERSIONS['ebook converter'] = _(converter.get_calibre_version())
_VERSIONS['unrar'] = _(converter.get_unrar_version())
_VERSIONS['kepubify'] = _(converter.get_kepubify_version())
return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=_VERSIONS, return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=_VERSIONS,
categorycounter=categorys, seriecounter=series, title=_(u"Statistics"), page="stat") categorycounter=categorys, seriecounter=series, title=_(u"Statistics"), page="stat")
This diff is collapsed.
...@@ -18,10 +18,17 @@ ...@@ -18,10 +18,17 @@
from __future__ import division, print_function, unicode_literals from __future__ import division, print_function, unicode_literals
import os import os
import io
from . import logger, isoLanguages from . import logger, isoLanguages
from .constants import BookMeta from .constants import BookMeta
try:
from PIL import Image as PILImage
use_PIL = True
except ImportError as e:
use_PIL = False
log = logger.create() log = logger.create()
...@@ -29,18 +36,43 @@ log = logger.create() ...@@ -29,18 +36,43 @@ log = logger.create()
try: try:
from comicapi.comicarchive import ComicArchive, MetaDataStyle from comicapi.comicarchive import ComicArchive, MetaDataStyle
use_comic_meta = True use_comic_meta = True
try:
from comicapi import __version__ as comic_version
except (ImportError):
comic_version = ''
except ImportError as e: except ImportError as e:
log.debug('cannot import comicapi, extracting comic metadata will not work: %s', e) log.debug('Cannot import comicapi, extracting comic metadata will not work: %s', e)
import zipfile import zipfile
import tarfile import tarfile
try: try:
import rarfile import rarfile
use_rarfile = True use_rarfile = True
except ImportError as e: except ImportError as e:
log.debug('cannot import rarfile, extracting cover files from rar files will not work: %s', e) log.debug('Cannot import rarfile, extracting cover files from rar files will not work: %s', e)
use_rarfile = False use_rarfile = False
use_comic_meta = False use_comic_meta = False
def _cover_processing(tmp_file_name, img, extension):
if use_PIL:
# convert to jpg because calibre only supports jpg
if extension in ('.png', '.webp'):
imgc = PILImage.open(io.BytesIO(img))
im = imgc.convert('RGB')
tmp_bytesio = io.BytesIO()
im.save(tmp_bytesio, format='JPEG')
img = tmp_bytesio.getvalue()
prefix = os.path.dirname(tmp_file_name)
if img:
tmp_cover_name = prefix + '/cover.jpg'
image = open(tmp_cover_name, 'wb')
image.write(img)
image.close()
else:
tmp_cover_name = None
return tmp_cover_name
def _extractCover(tmp_file_name, original_file_extension, rarExceutable): def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
cover_data = extension = None cover_data = extension = None
...@@ -50,7 +82,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable): ...@@ -50,7 +82,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension == '.jpg' or extension == '.jpeg': if extension in ('.jpg', '.jpeg', '.png', '.webp'):
cover_data = archive.getPage(index) cover_data = archive.getPage(index)
break break
else: else:
...@@ -60,7 +92,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable): ...@@ -60,7 +92,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension == '.jpg' or extension == '.jpeg': if extension in ('.jpg', '.jpeg', '.png', '.webp'):
cover_data = cf.read(name) cover_data = cf.read(name)
break break
elif original_file_extension.upper() == '.CBT': elif original_file_extension.upper() == '.CBT':
...@@ -69,7 +101,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable): ...@@ -69,7 +101,7 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension == '.jpg' or extension == '.jpeg': if extension in ('.jpg', '.jpeg', '.png', '.webp'):
cover_data = cf.extractfile(name).read() cover_data = cf.extractfile(name).read()
break break
elif original_file_extension.upper() == '.CBR' and use_rarfile: elif original_file_extension.upper() == '.CBR' and use_rarfile:
...@@ -80,21 +112,12 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable): ...@@ -80,21 +112,12 @@ def _extractCover(tmp_file_name, original_file_extension, rarExceutable):
ext = os.path.splitext(name) ext = os.path.splitext(name)
if len(ext) > 1: if len(ext) > 1:
extension = ext[1].lower() extension = ext[1].lower()
if extension == '.jpg' or extension == '.jpeg': if extension in ('.jpg', '.jpeg', '.png', '.webp'):
cover_data = cf.read(name) cover_data = cf.read(name)
break break
except Exception as e: except Exception as e:
log.debug('Rarfile failed with error: %s', e) log.debug('Rarfile failed with error: %s', e)
return _cover_processing(tmp_file_name, cover_data, extension)
prefix = os.path.dirname(tmp_file_name)
if cover_data:
tmp_cover_name = prefix + '/cover' + extension
image = open(tmp_cover_name, 'wb')
image.write(cover_data)
image.close()
else:
tmp_cover_name = None
return tmp_cover_name
def get_comic_info(tmp_file_path, original_file_name, original_file_extension, rarExceutable): def get_comic_info(tmp_file_path, original_file_name, original_file_extension, rarExceutable):
......
...@@ -53,6 +53,7 @@ class _Settings(_Base): ...@@ -53,6 +53,7 @@ class _Settings(_Base):
mail_login = Column(String, default='mail@example.com') mail_login = Column(String, default='mail@example.com')
mail_password = Column(String, default='mypassword') mail_password = Column(String, default='mypassword')
mail_from = Column(String, default='automailer <mail@example.com>') mail_from = Column(String, default='automailer <mail@example.com>')
mail_size = Column(Integer, default=25*1024*1024)
config_calibre_dir = Column(String) config_calibre_dir = Column(String)
config_port = Column(Integer, default=constants.DEFAULT_PORT) config_port = Column(Integer, default=constants.DEFAULT_PORT)
...@@ -96,7 +97,7 @@ class _Settings(_Base): ...@@ -96,7 +97,7 @@ class _Settings(_Base):
config_use_goodreads = Column(Boolean, default=False) config_use_goodreads = Column(Boolean, default=False)
config_goodreads_api_key = Column(String) config_goodreads_api_key = Column(String)
config_goodreads_api_secret = Column(String) config_goodreads_api_secret = Column(String)
config_register_email = Column(Boolean, default=False)
config_login_type = Column(Integer, default=0) config_login_type = Column(Integer, default=0)
config_kobo_proxy = Column(Boolean, default=False) config_kobo_proxy = Column(Boolean, default=False)
...@@ -116,10 +117,11 @@ class _Settings(_Base): ...@@ -116,10 +117,11 @@ class _Settings(_Base):
config_ldap_group_members_field = Column(String, default='memberUid') config_ldap_group_members_field = Column(String, default='memberUid')
config_ldap_group_name = Column(String, default='calibreweb') config_ldap_group_name = Column(String, default='calibreweb')
config_ebookconverter = Column(Integer, default=0) config_kepubifypath = Column(String, default=None)
config_converterpath = Column(String) config_converterpath = Column(String, default=None)
config_calibre = Column(String) config_calibre = Column(String)
config_rarfile_location = Column(String) config_rarfile_location = Column(String, default=None)
config_upload_formats = Column(String, default=','.join(constants.EXTENSIONS_UPLOAD))
config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE) config_updatechannel = Column(Integer, default=constants.UPDATE_STABLE)
...@@ -140,6 +142,22 @@ class _ConfigSQL(object): ...@@ -140,6 +142,22 @@ class _ConfigSQL(object):
self.config_calibre_dir = None self.config_calibre_dir = None
self.load() self.load()
change = False
if self.config_converterpath == None:
change = True
self.config_converterpath = autodetect_calibre_binary()
if self.config_kepubifypath == None:
change = True
self.config_kepubifypath = autodetect_kepubify_binary()
if self.config_rarfile_location == None:
change = True
self.config_rarfile_location = autodetect_unrar_binary()
if change:
self.save()
def _read_from_storage(self): def _read_from_storage(self):
if self._settings is None: if self._settings is None:
log.debug("_ConfigSQL._read_from_storage") log.debug("_ConfigSQL._read_from_storage")
...@@ -264,7 +282,8 @@ class _ConfigSQL(object): ...@@ -264,7 +282,8 @@ class _ConfigSQL(object):
setattr(self, k, v) setattr(self, k, v)
if self.config_google_drive_watch_changes_response: if self.config_google_drive_watch_changes_response:
self.config_google_drive_watch_changes_response = json.loads(self.config_google_drive_watch_changes_response) self.config_google_drive_watch_changes_response = \
json.loads(self.config_google_drive_watch_changes_response)
have_metadata_db = bool(self.config_calibre_dir) have_metadata_db = bool(self.config_calibre_dir)
if have_metadata_db: if have_metadata_db:
...@@ -272,8 +291,13 @@ class _ConfigSQL(object): ...@@ -272,8 +291,13 @@ class _ConfigSQL(object):
db_file = os.path.join(self.config_calibre_dir, 'metadata.db') db_file = os.path.join(self.config_calibre_dir, 'metadata.db')
have_metadata_db = os.path.isfile(db_file) have_metadata_db = os.path.isfile(db_file)
self.db_configured = have_metadata_db self.db_configured = have_metadata_db
constants.EXTENSIONS_UPLOAD = [x.lstrip().rstrip() for x in self.config_upload_formats.split(',')]
logger.setup(self.config_logfile, self.config_log_level) logfile = logger.setup(self.config_logfile, self.config_log_level)
if logfile != self.config_logfile:
log.warning("Log path %s not valid, falling back to default", self.config_logfile)
self.config_logfile = logfile
self._session.merge(s)
self._session.commit()
def save(self): def save(self):
'''Apply all configuration values to the underlying storage.''' '''Apply all configuration values to the underlying storage.'''
...@@ -334,17 +358,41 @@ def _migrate_table(session, orm_class): ...@@ -334,17 +358,41 @@ def _migrate_table(session, orm_class):
if changed: if changed:
session.commit() session.commit()
def autodetect_calibre_binary(): def autodetect_calibre_binary():
if sys.platform == "win32": if sys.platform == "win32":
calibre_path = ["C:\\program files\calibre\calibre-convert.exe", calibre_path = ["C:\\program files\calibre\ebook-convert.exe",
"C:\\program files(x86)\calibre\calibre-convert.exe"] "C:\\program files(x86)\calibre\ebook-convert.exe",
"C:\\program files(x86)\calibre2\ebook-convert.exe",
"C:\\program files\calibre2\ebook-convert.exe"]
else: else:
calibre_path = ["/opt/calibre/ebook-convert"] calibre_path = ["/opt/calibre/ebook-convert"]
for element in calibre_path: for element in calibre_path:
if os.path.isfile(element) and os.access(element, os.X_OK): if os.path.isfile(element) and os.access(element, os.X_OK):
return element return element
return None return ""
def autodetect_unrar_binary():
if sys.platform == "win32":
calibre_path = ["C:\\program files\\WinRar\\unRAR.exe",
"C:\\program files(x86)\\WinRar\\unRAR.exe"]
else:
calibre_path = ["/usr/bin/unrar"]
for element in calibre_path:
if os.path.isfile(element) and os.access(element, os.X_OK):
return element
return ""
def autodetect_kepubify_binary():
if sys.platform == "win32":
calibre_path = ["C:\\program files\\kepubify\\kepubify-windows-64Bit.exe",
"C:\\program files(x86)\\kepubify\\kepubify-windows-64Bit.exe"]
else:
calibre_path = ["/opt/kepubify/kepubify-linux-64bit", "/opt/kepubify/kepubify-linux-32bit"]
for element in calibre_path:
if os.path.isfile(element) and os.access(element, os.X_OK):
return element
return ""
def _migrate_database(session): def _migrate_database(session):
# make sure the table is created, if it does not exist # make sure the table is created, if it does not exist
......
...@@ -81,6 +81,7 @@ SIDEBAR_PUBLISHER = 1 << 12 ...@@ -81,6 +81,7 @@ SIDEBAR_PUBLISHER = 1 << 12
SIDEBAR_RATING = 1 << 13 SIDEBAR_RATING = 1 << 13
SIDEBAR_FORMAT = 1 << 14 SIDEBAR_FORMAT = 1 << 14
SIDEBAR_ARCHIVED = 1 << 15 SIDEBAR_ARCHIVED = 1 << 15
# SIDEBAR_LIST = 1 << 16
ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS ADMIN_USER_ROLES = sum(r for r in ALL_ROLES.values()) & ~ROLE_ANONYMOUS
ADMIN_USER_SIDEBAR = (SIDEBAR_ARCHIVED << 1) - 1 ADMIN_USER_SIDEBAR = (SIDEBAR_ARCHIVED << 1) - 1
...@@ -111,11 +112,9 @@ del env_CALIBRE_PORT ...@@ -111,11 +112,9 @@ del env_CALIBRE_PORT
EXTENSIONS_AUDIO = {'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac'} EXTENSIONS_AUDIO = {'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac'}
EXTENSIONS_CONVERT = {'pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'htmlz', 'rtf', 'odt'} EXTENSIONS_CONVERT = ['pdf', 'epub', 'mobi', 'azw3', 'docx', 'rtf', 'fb2', 'lit', 'lrf', 'txt', 'htmlz', 'rtf', 'odt']
EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'kepub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx',
'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac'} 'fb2', 'html', 'rtf', 'lit', 'odt', 'mp3', 'mp4', 'ogg', 'opus', 'wav', 'flac'}
# EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] +
# (['rar','cbr'] if feature_support['rar'] else []))
def has_flag(value, bit_flag): def has_flag(value, bit_flag):
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
from __future__ import division, print_function, unicode_literals from __future__ import division, print_function, unicode_literals
import os import os
import re import re
import sys
from flask_babel import gettext as _ from flask_babel import gettext as _
from . import config, logger from . import config, logger
...@@ -29,8 +30,8 @@ log = logger.create() ...@@ -29,8 +30,8 @@ log = logger.create()
# _() necessary to make babel aware of string for translation # _() necessary to make babel aware of string for translation
_NOT_CONFIGURED = _('not configured') _NOT_CONFIGURED = _('not configured')
_NOT_INSTALLED = 'not installed' _NOT_INSTALLED = _('not installed')
_EXECUTION_ERROR = 'Execution permissions missing' _EXECUTION_ERROR = _('Execution permissions missing')
def _get_command_version(path, pattern, argument=None): def _get_command_version(path, pattern, argument=None):
...@@ -48,10 +49,15 @@ def _get_command_version(path, pattern, argument=None): ...@@ -48,10 +49,15 @@ def _get_command_version(path, pattern, argument=None):
return _NOT_INSTALLED return _NOT_INSTALLED
def get_version(): def get_calibre_version():
version = None return _get_command_version(config.config_converterpath, r'ebook-convert.*\(calibre', '--version') \
if config.config_ebookconverter == 1: or _NOT_CONFIGURED
version = _get_command_version(config.config_converterpath, r'Amazon kindlegen\(')
elif config.config_ebookconverter == 2:
version = _get_command_version(config.config_converterpath, r'ebook-convert.*\(calibre', '--version') def get_unrar_version():
return version or _NOT_CONFIGURED return _get_command_version(config.config_rarfile_location, r'UNRAR.*\d') or _NOT_CONFIGURED
def get_kepubify_version():
return _get_command_version(config.config_kepubifypath, r'kepubify\s','--version') or _NOT_CONFIGURED
This diff is collapsed.
This diff is collapsed.
...@@ -39,7 +39,7 @@ try: ...@@ -39,7 +39,7 @@ try:
except ImportError: except ImportError:
pass pass
from . import logger, gdriveutils, config, db from . import logger, gdriveutils, config, ub, calibre_db
from .web import admin_required from .web import admin_required
...@@ -145,7 +145,8 @@ def on_received_watch_confirmation(): ...@@ -145,7 +145,8 @@ def on_received_watch_confirmation():
dbpath = os.path.join(config.config_calibre_dir, "metadata.db") dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
else: else:
dbpath = os.path.join(config.config_calibre_dir, "metadata.db").encode() dbpath = os.path.join(config.config_calibre_dir, "metadata.db").encode()
if not response['deleted'] and response['file']['title'] == 'metadata.db' and response['file']['md5Checksum'] != hashlib.md5(dbpath): if not response['deleted'] and response['file']['title'] == 'metadata.db' \
and response['file']['md5Checksum'] != hashlib.md5(dbpath):
tmpDir = tempfile.gettempdir() tmpDir = tempfile.gettempdir()
log.info('Database file updated') log.info('Database file updated')
copyfile(dbpath, os.path.join(tmpDir, "metadata.db_" + str(current_milli_time()))) copyfile(dbpath, os.path.join(tmpDir, "metadata.db_" + str(current_milli_time())))
...@@ -154,7 +155,7 @@ def on_received_watch_confirmation(): ...@@ -154,7 +155,7 @@ def on_received_watch_confirmation():
log.info('Setting up new DB') log.info('Setting up new DB')
# prevent error on windows, as os.rename does on exisiting files # prevent error on windows, as os.rename does on exisiting files
move(os.path.join(tmpDir, "tmp_metadata.db"), dbpath) move(os.path.join(tmpDir, "tmp_metadata.db"), dbpath)
db.setup_db(config) calibre_db.setup_db(config, ub.app_DB_path)
except Exception as e: except Exception as e:
log.exception(e) log.exception(e)
updateMetaData() updateMetaData()
......
This diff is collapsed.
...@@ -57,12 +57,12 @@ def get_language_name(locale, lang_code): ...@@ -57,12 +57,12 @@ def get_language_name(locale, lang_code):
def get_language_codes(locale, language_names, remainder=None): def get_language_codes(locale, language_names, remainder=None):
language_names = set(x.strip().lower() for x in language_names if x) language_names = set(x.strip().lower() for x in language_names if x)
languages = list()
for k, v in get_language_names(locale).items(): for k, v in get_language_names(locale).items():
v = v.lower() v = v.lower()
if v in language_names: if v in language_names:
languages.append(k)
language_names.remove(v) language_names.remove(v)
yield k
if remainder is not None: if remainder is not None:
remainder.extend(language_names) remainder.extend(language_names)
return languages
...@@ -111,10 +111,3 @@ def timestamptodate(date, fmt=None): ...@@ -111,10 +111,3 @@ def timestamptodate(date, fmt=None):
@jinjia.app_template_filter('yesno') @jinjia.app_template_filter('yesno')
def yesno(value, yes, no): def yesno(value, yes, no):
return yes if value else no return yes if value else no
'''@jinjia.app_template_filter('canread')
def canread(ext):
if isinstance(ext, db.Data):
ext = ext.format
return ext.lower() in EXTENSIONS_READER'''
...@@ -48,7 +48,7 @@ from sqlalchemy.sql.expression import and_, or_ ...@@ -48,7 +48,7 @@ from sqlalchemy.sql.expression import and_, or_
from sqlalchemy.exc import StatementError from sqlalchemy.exc import StatementError
import requests import requests
from . import config, logger, kobo_auth, db, helper, shelf as shelf_lib, ub from . import config, logger, kobo_auth, db, calibre_db, helper, shelf as shelf_lib, ub
from .services import SyncToken as SyncToken from .services import SyncToken as SyncToken
from .web import download_required from .web import download_required
from .kobo_auth import requires_kobo_auth from .kobo_auth import requires_kobo_auth
...@@ -143,7 +143,7 @@ def HandleSyncRequest(): ...@@ -143,7 +143,7 @@ def HandleSyncRequest():
# We reload the book database so that the user get's a fresh view of the library # We reload the book database so that the user get's a fresh view of the library
# in case of external changes (e.g: adding a book through Calibre). # in case of external changes (e.g: adding a book through Calibre).
db.reconnect_db(config) calibre_db.reconnect_db(config, ub.app_DB_path)
archived_books = ( archived_books = (
ub.session.query(ub.ArchivedBook) ub.session.query(ub.ArchivedBook)
...@@ -170,7 +170,7 @@ def HandleSyncRequest(): ...@@ -170,7 +170,7 @@ def HandleSyncRequest():
# It looks like it's treating the db.Books.last_modified field as a string and may fail # It looks like it's treating the db.Books.last_modified field as a string and may fail
# the comparison because of the +00:00 suffix. # the comparison because of the +00:00 suffix.
changed_entries = ( changed_entries = (
db.session.query(db.Books) calibre_db.session.query(db.Books)
.join(db.Data) .join(db.Data)
.filter(or_(func.datetime(db.Books.last_modified) > sync_token.books_last_modified, .filter(or_(func.datetime(db.Books.last_modified) > sync_token.books_last_modified,
db.Books.id.in_(recently_restored_or_archived_books))) db.Books.id.in_(recently_restored_or_archived_books)))
...@@ -207,7 +207,7 @@ def HandleSyncRequest(): ...@@ -207,7 +207,7 @@ def HandleSyncRequest():
ub.KoboReadingState.user_id == current_user.id, ub.KoboReadingState.user_id == current_user.id,
ub.KoboReadingState.book_id.notin_(reading_states_in_new_entitlements)))) ub.KoboReadingState.book_id.notin_(reading_states_in_new_entitlements))))
for kobo_reading_state in changed_reading_states.all(): for kobo_reading_state in changed_reading_states.all():
book = db.session.query(db.Books).filter(db.Books.id == kobo_reading_state.book_id).one_or_none() book = calibre_db.session.query(db.Books).filter(db.Books.id == kobo_reading_state.book_id).one_or_none()
if book: if book:
sync_results.append({ sync_results.append({
"ChangedReadingState": { "ChangedReadingState": {
...@@ -256,7 +256,7 @@ def HandleMetadataRequest(book_uuid): ...@@ -256,7 +256,7 @@ def HandleMetadataRequest(book_uuid):
if not current_app.wsgi_app.is_proxied: if not current_app.wsgi_app.is_proxied:
log.debug('Kobo: Received unproxied request, changed request port to server port') log.debug('Kobo: Received unproxied request, changed request port to server port')
log.info("Kobo library metadata request received for book %s" % book_uuid) log.info("Kobo library metadata request received for book %s" % book_uuid)
book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() book = calibre_db.get_book_by_uuid(book_uuid)
if not book or not book.data: if not book or not book.data:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()
...@@ -474,7 +474,7 @@ def add_items_to_shelf(items, shelf): ...@@ -474,7 +474,7 @@ def add_items_to_shelf(items, shelf):
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() book = calibre_db.get_book_by_uuid(item["RevisionId"])
if not book: if not book:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
...@@ -545,7 +545,7 @@ def HandleTagRemoveItem(tag_id): ...@@ -545,7 +545,7 @@ def HandleTagRemoveItem(tag_id):
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
book = db.session.query(db.Books).filter(db.Books.uuid == item["RevisionId"]).one_or_none() book = calibre_db.get_book_by_uuid(item["RevisionId"])
if not book: if not book:
items_unknown_to_calibre.append(item) items_unknown_to_calibre.append(item)
continue continue
...@@ -613,7 +613,7 @@ def create_kobo_tag(shelf): ...@@ -613,7 +613,7 @@ def create_kobo_tag(shelf):
"Type": "UserTag" "Type": "UserTag"
} }
for book_shelf in shelf.books: for book_shelf in shelf.books:
book = db.session.query(db.Books).filter(db.Books.id == book_shelf.book_id).one_or_none() book = calibre_db.get_book(book_shelf.book_id)
if not book: if not book:
log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id) log.info(u"Book (id: %s) in BookShelf (id: %s) not found in book database", book_shelf.book_id, shelf.id)
continue continue
...@@ -629,7 +629,7 @@ def create_kobo_tag(shelf): ...@@ -629,7 +629,7 @@ def create_kobo_tag(shelf):
@kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"]) @kobo.route("/v1/library/<book_uuid>/state", methods=["GET", "PUT"])
@login_required @login_required
def HandleStateRequest(book_uuid): def HandleStateRequest(book_uuid):
book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() book = calibre_db.get_book_by_uuid(book_uuid)
if not book or not book.data: if not book or not book.data:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()
...@@ -804,7 +804,7 @@ def TopLevelEndpoint(): ...@@ -804,7 +804,7 @@ def TopLevelEndpoint():
@login_required @login_required
def HandleBookDeletionRequest(book_uuid): def HandleBookDeletionRequest(book_uuid):
log.info("Kobo book deletion request received for book %s" % book_uuid) log.info("Kobo book deletion request received for book %s" % book_uuid)
book = db.session.query(db.Books).filter(db.Books.uuid == book_uuid).first() book = calibre_db.get_book_by_uuid(book_uuid)
if not book: if not book:
log.info(u"Book %s not found in database", book_uuid) log.info(u"Book %s not found in database", book_uuid)
return redirect_or_proxy_request() return redirect_or_proxy_request()
......
...@@ -82,7 +82,6 @@ def _absolute_log_file(log_file, default_log_file): ...@@ -82,7 +82,6 @@ def _absolute_log_file(log_file, default_log_file):
if not os.path.dirname(log_file): if not os.path.dirname(log_file):
log_file = os.path.join(_CONFIG_DIR, log_file) log_file = os.path.join(_CONFIG_DIR, log_file)
return os.path.abspath(log_file) return os.path.abspath(log_file)
return default_log_file return default_log_file
...@@ -115,7 +114,7 @@ def setup(log_file, log_level=None): ...@@ -115,7 +114,7 @@ def setup(log_file, log_level=None):
if previous_handler: if previous_handler:
# if the log_file has not changed, don't create a new handler # if the log_file has not changed, don't create a new handler
if getattr(previous_handler, 'baseFilename', None) == log_file: if getattr(previous_handler, 'baseFilename', None) == log_file:
return return "" if log_file == DEFAULT_LOG_FILE else log_file
logging.debug("logging to %s level %s", log_file, r.level) logging.debug("logging to %s level %s", log_file, r.level)
if log_file == LOG_TO_STDERR or log_file == LOG_TO_STDOUT: if log_file == LOG_TO_STDERR or log_file == LOG_TO_STDOUT:
...@@ -132,12 +131,14 @@ def setup(log_file, log_level=None): ...@@ -132,12 +131,14 @@ def setup(log_file, log_level=None):
if log_file == DEFAULT_LOG_FILE: if log_file == DEFAULT_LOG_FILE:
raise raise
file_handler = RotatingFileHandler(DEFAULT_LOG_FILE, maxBytes=50000, backupCount=2) file_handler = RotatingFileHandler(DEFAULT_LOG_FILE, maxBytes=50000, backupCount=2)
log_file = ""
file_handler.setFormatter(FORMATTER) file_handler.setFormatter(FORMATTER)
for h in r.handlers: for h in r.handlers:
r.removeHandler(h) r.removeHandler(h)
h.close() h.close()
r.addHandler(file_handler) r.addHandler(file_handler)
return "" if log_file == DEFAULT_LOG_FILE else log_file
def create_access_log(log_file, log_name, formatter): def create_access_log(log_file, log_name, formatter):
...@@ -150,11 +151,18 @@ def create_access_log(log_file, log_name, formatter): ...@@ -150,11 +151,18 @@ def create_access_log(log_file, log_name, formatter):
access_log = logging.getLogger(log_name) access_log = logging.getLogger(log_name)
access_log.propagate = False access_log.propagate = False
access_log.setLevel(logging.INFO) access_log.setLevel(logging.INFO)
try:
file_handler = RotatingFileHandler(log_file, maxBytes=50000, backupCount=2)
except IOError:
if log_file == DEFAULT_ACCESS_LOG:
raise
file_handler = RotatingFileHandler(DEFAULT_ACCESS_LOG, maxBytes=50000, backupCount=2)
log_file = ""
file_handler = RotatingFileHandler(log_file, maxBytes=50000, backupCount=2)
file_handler.setFormatter(formatter) file_handler.setFormatter(formatter)
access_log.addHandler(file_handler) access_log.addHandler(file_handler)
return access_log return access_log, \
"" if _absolute_log_file(log_file, DEFAULT_ACCESS_LOG) == DEFAULT_ACCESS_LOG else log_file
# Enable logging of smtp lib debug output # Enable logging of smtp lib debug output
......
This diff is collapsed.
...@@ -72,7 +72,11 @@ class WebServer(object): ...@@ -72,7 +72,11 @@ class WebServer(object):
if config.config_access_log: if config.config_access_log:
log_name = "gevent.access" if _GEVENT else "tornado.access" log_name = "gevent.access" if _GEVENT else "tornado.access"
formatter = logger.ACCESS_FORMATTER_GEVENT if _GEVENT else logger.ACCESS_FORMATTER_TORNADO formatter = logger.ACCESS_FORMATTER_GEVENT if _GEVENT else logger.ACCESS_FORMATTER_TORNADO
self.access_logger = logger.create_access_log(config.config_access_logfile, log_name, formatter) self.access_logger, logfile = logger.create_access_log(config.config_access_logfile, log_name, formatter)
if logfile != config.config_access_logfile:
log.warning("Accesslog path %s not valid, falling back to default", config.config_access_logfile)
config.config_access_logfile = logfile
config.save()
else: else:
if not _GEVENT: if not _GEVENT:
logger.get('tornado.access').disabled = True logger.get('tornado.access').disabled = True
...@@ -196,6 +200,9 @@ class WebServer(object): ...@@ -196,6 +200,9 @@ class WebServer(object):
def stop(self, restart=False): def stop(self, restart=False):
from . import updater_thread from . import updater_thread
updater_thread.stop() updater_thread.stop()
from . import calibre_db
calibre_db.stop()
log.info("webserver stop (restart=%s)", restart) log.info("webserver stop (restart=%s)", restart)
self.restart = restart self.restart = restart
......
...@@ -20,7 +20,10 @@ from __future__ import division, print_function, unicode_literals ...@@ -20,7 +20,10 @@ from __future__ import division, print_function, unicode_literals
import time import time
from functools import reduce from functools import reduce
from goodreads.client import GoodreadsClient try:
from goodreads.client import GoodreadsClient
except ImportError:
from betterreads.client import GoodreadsClient
try: import Levenshtein try: import Levenshtein
except ImportError: Levenshtein = False except ImportError: Levenshtein = False
...@@ -95,8 +98,12 @@ def get_other_books(author_info, library_books=None): ...@@ -95,8 +98,12 @@ def get_other_books(author_info, library_books=None):
for book in author_info.books: for book in author_info.books:
if book.isbn in identifiers: if book.isbn in identifiers:
continue continue
if book.gid["#text"] in identifiers: if isinstance(book.gid, int):
continue if book.gid in identifiers:
continue
else:
if book.gid["#text"] in identifiers:
continue
if Levenshtein and library_titles: if Levenshtein and library_titles:
goodreads_title = book._book_dict['title_without_series'] goodreads_title = book._book_dict['title_without_series']
......
...@@ -28,9 +28,8 @@ from flask_babel import gettext as _ ...@@ -28,9 +28,8 @@ from flask_babel import gettext as _
from flask_login import login_required, current_user from flask_login import login_required, current_user
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from . import logger, ub, searched_ids, db from . import logger, ub, searched_ids, db, calibre_db
from .web import render_title_template from .web import render_title_template
from .helper import common_filters
shelf = Blueprint('shelf', __name__) shelf = Blueprint('shelf', __name__)
...@@ -320,11 +319,11 @@ def show_shelf(shelf_type, shelf_id): ...@@ -320,11 +319,11 @@ def show_shelf(shelf_type, shelf_id):
books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\ books_in_shelf = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id)\
.order_by(ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf: for book in books_in_shelf:
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() cur_book = calibre_db.get_filtered_book(book.book_id)
if cur_book: if cur_book:
result.append(cur_book) result.append(cur_book)
else: else:
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() cur_book = calibre_db.get_book(book.book_id)
if not cur_book: if not cur_book:
log.info('Not existing book %s in %s deleted', book.book_id, shelf) log.info('Not existing book %s in %s deleted', book.book_id, shelf)
ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book.book_id).delete() ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book.book_id).delete()
...@@ -356,7 +355,7 @@ def order_shelf(shelf_id): ...@@ -356,7 +355,7 @@ def order_shelf(shelf_id):
books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \ books_in_shelf2 = ub.session.query(ub.BookShelf).filter(ub.BookShelf.shelf == shelf_id) \
.order_by(ub.BookShelf.order.asc()).all() .order_by(ub.BookShelf.order.asc()).all()
for book in books_in_shelf2: for book in books_in_shelf2:
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).filter(common_filters()).first() cur_book = calibre_db.get_filtered_book(book.book_id)
if cur_book: if cur_book:
result.append({'title': cur_book.title, result.append({'title': cur_book.title,
'id': cur_book.id, 'id': cur_book.id,
...@@ -364,7 +363,7 @@ def order_shelf(shelf_id): ...@@ -364,7 +363,7 @@ def order_shelf(shelf_id):
'series': cur_book.series, 'series': cur_book.series,
'series_index': cur_book.series_index}) 'series_index': cur_book.series_index})
else: else:
cur_book = db.session.query(db.Books).filter(db.Books.id == book.book_id).first() cur_book = calibre_db.get_book(book.book_id)
result.append({'title': _('Hidden Book'), result.append({'title': _('Hidden Book'),
'id': cur_book.id, 'id': cur_book.id,
'author': [], 'author': [],
......
body.serieslist.grid-view div.container-fluid>div>div.col-sm-10:before{
display: none;
}
.cover .badge{
position: absolute;
top: 0;
right: 0;
background-color: #cc7b19;
border-radius: 0;
padding: 0 8px;
box-shadow: 0 0 4px rgba(0,0,0,.6);
line-height: 24px;
}
.cover{
box-shadow: 0 0 4px rgba(0,0,0,.6);
}
...@@ -109,7 +109,7 @@ a { color: #45b29d; } ...@@ -109,7 +109,7 @@ a { color: #45b29d; }
.container-fluid .book .cover img { .container-fluid .book .cover img {
border: 1px solid #fff; border: 1px solid #fff;
box-sizeing: border-box; box-sizing: border-box;
height: 100%; height: 100%;
bottom: 0; bottom: 0;
position: absolute; position: absolute;
...@@ -165,7 +165,7 @@ span.glyphicon.glyphicon-tags { ...@@ -165,7 +165,7 @@ span.glyphicon.glyphicon-tags {
.container-fluid .single .cover img { .container-fluid .single .cover img {
border: 1px solid #fff; border: 1px solid #fff;
box-sizeing: border-box; box-sizing: border-box;
-webkit-box-shadow: 0 5px 8px -6px #777; -webkit-box-shadow: 0 5px 8px -6px #777;
-moz-box-shadow: 0 5px 8px -6px #777; -moz-box-shadow: 0 5px 8px -6px #777;
box-shadow: 0 5px 8px -6px #777; box-shadow: 0 5px 8px -6px #777;
...@@ -174,6 +174,12 @@ span.glyphicon.glyphicon-tags { ...@@ -174,6 +174,12 @@ span.glyphicon.glyphicon-tags {
.navbar-default .navbar-toggle .icon-bar {background-color: #000; } .navbar-default .navbar-toggle .icon-bar {background-color: #000; }
.navbar-default .navbar-toggle {border-color: #000; } .navbar-default .navbar-toggle {border-color: #000; }
.cover { margin-bottom: 10px; } .cover { margin-bottom: 10px; }
.cover .badge{
position: absolute;
top: 2px;
left: 2px;
background-color: #777;
}
.cover-height { max-height: 100px;} .cover-height { max-height: 100px;}
.col-sm-2 a .cover-small { .col-sm-2 a .cover-small {
...@@ -207,7 +213,7 @@ span.glyphicon.glyphicon-tags { ...@@ -207,7 +213,7 @@ span.glyphicon.glyphicon-tags {
.panel-body {background-color: #f5f5f5; } .panel-body {background-color: #f5f5f5; }
.spinner {margin: 0 41%; } .spinner {margin: 0 41%; }
.spinner2 {margin: 0 41%; } .spinner2 {margin: 0 41%; }
.intend-form { margin-left:20px; }
table .bg-dark-danger {background-color: #d9534f; color: #fff; } table .bg-dark-danger {background-color: #d9534f; color: #fff; }
table .bg-dark-danger a {color: #fff; } table .bg-dark-danger a {color: #fff; }
table .bg-dark-danger:hover {background-color: #c9302c; } table .bg-dark-danger:hover {background-color: #c9302c; }
...@@ -296,3 +302,4 @@ div.log { ...@@ -296,3 +302,4 @@ div.log {
white-space: nowrap; white-space: nowrap;
padding: 0.5em; padding: 0.5em;
} }
/* This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
* Copyright (C) 2018 OzzieIsaacs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var $list = $("#list").isotope({
itemSelector: ".book",
layoutMode: "fitRows",
getSortData: {
title: ".title",
}
});
$("#desc").click(function() {
$list.isotope({
sortBy: "name",
sortAscending: true
});
return;
});
$("#asc").click(function() {
$list.isotope({
sortBy: "name",
sortAscending: false
});
return;
});
$("#all").click(function() {
// go through all elements and make them visible
$list.isotope({ filter: function() {
return true;
} })
});
$(".char").click(function() {
var character = this.innerText;
$list.isotope({ filter: function() {
return this.attributes["data-id"].value.charAt(0).toUpperCase() == character;
} })
});
...@@ -323,4 +323,17 @@ $(function() { ...@@ -323,4 +323,17 @@ $(function() {
$(".discover .row").isotope("layout"); $(".discover .row").isotope("layout");
}); });
$(".update-view").click(function(e) {
var target = $(this).data("target");
var view = $(this).data("view");
e.preventDefault();
e.stopPropagation();
var data = {};
data[target] = view;
console.debug("Updating view data: ", data);
$.post( "/ajax/view", data).done(function( ) {
location.reload();
});
});
}); });
...@@ -22,7 +22,7 @@ import os ...@@ -22,7 +22,7 @@ import os
import subprocess import subprocess
def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subprocess.PIPE): def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subprocess.PIPE, newlines=True):
# Linux py2.7 encode as list without quotes no empty element for parameters # Linux py2.7 encode as list without quotes no empty element for parameters
# linux py3.x no encode and as list without quotes no empty element for parameters # linux py3.x no encode and as list without quotes no empty element for parameters
# windows py2.7 encode as string with quotes empty element for parameters is okay # windows py2.7 encode as string with quotes empty element for parameters is okay
...@@ -41,12 +41,13 @@ def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subpro ...@@ -41,12 +41,13 @@ def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subpro
else: else:
exc_command = [x for x in command] exc_command = [x for x in command]
return subprocess.Popen(exc_command, shell=False, stdout=sout, stderr=serr, universal_newlines=True, env=env) return subprocess.Popen(exc_command, shell=False, stdout=sout, stderr=serr, universal_newlines=newlines, env=env)
def process_wait(command, serr=subprocess.PIPE): def process_wait(command, serr=subprocess.PIPE):
# Run command, wait for process to terminate, and return an iterator over lines of its output. # Run command, wait for process to terminate, and return an iterator over lines of its output.
p = process_open(command, serr=serr) newlines = os.name != 'nt'
p = process_open(command, serr=serr, newlines=newlines)
p.wait() p.wait()
for line in p.stdout.readlines(): for line in p.stdout.readlines():
if isinstance(line, bytes): if isinstance(line, bytes):
......
...@@ -13,11 +13,14 @@ ...@@ -13,11 +13,14 @@
<th>{{_('E-mail Address')}}</th> <th>{{_('E-mail Address')}}</th>
<th>{{_('Send to Kindle E-mail Address')}}</th> <th>{{_('Send to Kindle E-mail Address')}}</th>
<th>{{_('Downloads')}}</th> <th>{{_('Downloads')}}</th>
<th class="hidden-xs">{{_('Admin')}}</th> <th class="hidden-xs ">{{_('Admin')}}</th>
<th class="hidden-xs">{{_('Download')}}</th> <th class="hidden-xs hidden-sm">{{_('Password')}}</th>
<th class="hidden-xs">{{_('View Books')}}</th> <th class="hidden-xs hidden-sm">{{_('Upload')}}</th>
<th class="hidden-xs">{{_('Upload')}}</th> <th class="hidden-xs hidden-sm">{{_('Download')}}</th>
<th class="hidden-xs">{{_('Edit')}}</th> <th class="hidden-xs hidden-sm hidden-md">{{_('View Books')}}</th>
<th class="hidden-xs hidden-sm hidden-md">{{_('Edit')}}</th>
<th class="hidden-xs hidden-sm hidden-md">{{_('Delete')}}</th>
<th class="hidden-xs hidden-sm hidden-md">{{_('Public Shelf')}}</th>
</tr> </tr>
{% for user in allUser %} {% for user in allUser %}
{% if not user.role_anonymous() or config.config_anonbrowse %} {% if not user.role_anonymous() or config.config_anonbrowse %}
...@@ -27,10 +30,13 @@ ...@@ -27,10 +30,13 @@
<td>{{user.kindle_mail}}</td> <td>{{user.kindle_mail}}</td>
<td>{{user.downloads.count()}}</td> <td>{{user.downloads.count()}}</td>
<td class="hidden-xs">{{ display_bool_setting(user.role_admin()) }}</td> <td class="hidden-xs">{{ display_bool_setting(user.role_admin()) }}</td>
<td class="hidden-xs">{{ display_bool_setting(user.role_download()) }}</td> <td class="hidden-xs hidden-sm">{{ display_bool_setting(user.role_passwd()) }}</td>
<td class="hidden-xs">{{ display_bool_setting(user.role_viewer()) }}</td> <td class="hidden-xs hidden-sm">{{ display_bool_setting(user.role_upload()) }}</td>
<td class="hidden-xs">{{ display_bool_setting(user.role_upload()) }}</td> <td class="hidden-xs hidden-sm">{{ display_bool_setting(user.role_download()) }}</td>
<td class="hidden-xs">{{ display_bool_setting(user.role_edit()) }}</td> <td class="hidden-xs hidden-sm hidden-md">{{ display_bool_setting(user.role_viewer()) }}</td>
<td class="hidden-xs hidden-sm hidden-md">{{ display_bool_setting(user.role_edit()) }}</td>
<td class="hidden-xs hidden-sm hidden-md">{{ display_bool_setting(user.role_delete_books()) }}</td>
<td class="hidden-xs hidden-sm hidden-md">{{ display_bool_setting(user.role_edit_shelfs()) }}</td>
</tr> </tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
......
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="series_index">{{_('Series ID')}}</label> <label for="series_index">{{_('Series ID')}}</label>
<input type="number" step="0.01" min="0" class="form-control" name="series_index" id="series_index" value="{{book.series_index}}"> <input type="number" step="0.01" min="0" placeholder="1" class="form-control" name="series_index" id="series_index" value="{{book.series_index}}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="rating">{{_('Rating')}}</label> <label for="rating">{{_('Rating')}}</label>
......
{% extends "layout.html" %}
{% block body %}
<h1 class="{{page}}">{{_(title)}}</h1>
<div class="filterheader hidden-xs hidden-sm">
{% if entries.__len__() %}
{% if data == 'author' %}
<button id="sort_name" class="btn btn-primary"><b>B,A <-> A B</b></button>
{% endif %}
{% endif %}
<button id="desc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
<button id="asc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
{% if charlist|length %}
<button id="all" class="btn btn-primary">{{_('All')}}</button>
{% endif %}
<div class="btn-group character" role="group">
{% for char in charlist%}
<button class="btn btn-primary char">{{char.char}}</button>
{% endfor %}
</div>
{% if title == "Series" %}
<button class="update-view btn btn-primary" href="#" data-target="series_view" data-view="grid">Grid</button>
{% endif %}
</div>
<div class="container">
<div id="list" class="col-xs-12 col-sm-6">
{% for entry in entries %}
{% if loop.index0 == (loop.length/2+loop.length%2)|int and loop.length > 20 %}
</div>
<div id="second" class="col-xs-12 col-sm-6">
{% endif %}
<div class="row" {% if entry[0].sort %}data-name="{{entry[0].name}}"{% endif %} data-id="{% if entry[0].sort %}{{entry[0].sort}}{% else %}{% if entry.name %}{{entry.name}}{% else %}{{entry[0].name}}{% endif %}{% endif %}">
<div class="col-xs-2 col-sm-2 col-md-1" align="left"><span class="badge">{{entry.count}}</span></div>
<div class="col-xs-10 col-sm-10 col-md-11"><a id="list_{{loop.index0}}" href="{% if entry.format %}{{url_for('web.books_list', data=data, sort='new', book_id=entry.format )}}{% else %}{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].id )}}{% endif %}">
{% if entry.name %}
<div class="rating">
{% for number in range(entry.name) %}
<span class="glyphicon glyphicon-star good"></span>
{% if loop.last and loop.index < 5 %}
{% for numer in range(5 - loop.index) %}
<span class="glyphicon glyphicon-star"></span>
{% endfor %}
{% endif %}
{% endfor %}
</div>
{% else %}
{% if entry.format %}
{{entry.format}}
{% else %}
{{entry[0].name}}{% endif %}{% endif %}</a></div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
{% block js %}
<script src="{{ url_for('static', filename='js/filter_list.js') }}"></script>
{% endblock %}
This diff is collapsed.
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
<h2>{{title}}</h2> <h2>{{title}}</h2>
<form role="form" method="POST" autocomplete="off"> <form role="form" method="POST" autocomplete="off" class="col-md-10 col-lg-6">
<div class="panel-group"> <div class="panel-group">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
......
...@@ -202,6 +202,7 @@ ...@@ -202,6 +202,7 @@
</label> </label>
</form> </form>
</p> </p>
{% if g.user.check_visibility(32768) %}
<p> <p>
<form id="archived_form" action="{{ url_for('web.toggle_archived', book_id=entry.id)}}" method="POST"> <form id="archived_form" action="{{ url_for('web.toggle_archived', book_id=entry.id)}}" method="POST">
<label class="block-label"> <label class="block-label">
...@@ -210,6 +211,7 @@ ...@@ -210,6 +211,7 @@
</label> </label>
</form> </form>
</p> </p>
{% endif %}
</div> </div>
{% endif %} {% endif %}
......
...@@ -6,14 +6,14 @@ ...@@ -6,14 +6,14 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
<h1>{{title}}</h1> <h1>{{title}}</h1>
<form role="form" method="POST"> <form role="form" class="col-md-10 col-lg-6" method="POST">
<div class="form-group"> <div class="form-group">
<label for="mail_server">{{_('SMTP Hostname')}}</label> <label for="mail_server">{{_('SMTP Hostname')}}</label>
<input type="text" class="form-control" name="mail_server" id="mail_server" value="{{content.mail_server}}"> <input type="text" class="form-control" name="mail_server" id="mail_server" value="{{content.mail_server}}">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail_port">{{_('SMTP Port')}}</label> <label for="mail_port">{{_('SMTP Port')}}</label>
<input type="text" class="form-control" name="mail_port" id="mail_port" value="{{content.mail_port}}"> <input type="number" min="1" max="65535" step="1" class="form-control" name="mail_port" id="mail_port" value="{% if content.mail_port != None %}{{ content.mail_port }}{% endif %}" autocomplete="off">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="mail_use_ssl">{{_('Encryption')}}</label> <label for="mail_use_ssl">{{_('Encryption')}}</label>
...@@ -35,11 +35,19 @@ ...@@ -35,11 +35,19 @@
<label for="mail_from">{{_('From E-mail')}}</label> <label for="mail_from">{{_('From E-mail')}}</label>
<input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}"> <input type="text" class="form-control" name="mail_from" id="mail_from" value="{{content.mail_from}}">
</div> </div>
<label for="mail_size">{{_('Attachment Size Limit')}}</label>
<div class="form-group input-group">
<input type="number" min="1" max="600" step="1" class="form-control" name="mail_size" id="mail_size" value="{% if content.mail_size != None %}{{ (content.mail_size / 1024 / 1024)|int }}{% endif %}">
<span class="input-group-btn">
<button type="button" id="attachement_size" class="btn btn-default" disabled>MB</button>
</span>
</div>
<button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save')}}</button> <button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save')}}</button>
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save and Send Test E-mail')}}</button> <button type="submit" name="test" value="test" class="btn btn-default">{{_('Save and Send Test E-mail')}}</button>
<a href="{{ url_for('admin.admin') }}" id="back" class="btn btn-default">{{_('Cancel')}}</a> <a href="{{ url_for('admin.admin') }}" id="back" class="btn btn-default">{{_('Cancel')}}</a>
</form> </form>
{% if g.allow_registration %} {% if g.allow_registration %}
<div class="col-md-10 col-lg-6">
<h2>{{_('Allowed Domains (Whitelist)')}}</h2> <h2>{{_('Allowed Domains (Whitelist)')}}</h2>
<form id="domain_add_allow" action="{{ url_for('admin.add_domain',allow=1)}}" method="POST"> <form id="domain_add_allow" action="{{ url_for('admin.add_domain',allow=1)}}" method="POST">
<div class="form-group required"> <div class="form-group required">
...@@ -74,6 +82,7 @@ ...@@ -74,6 +82,7 @@
</div> </div>
<button id="domain_deny_submit" class="btn btn-default">{{_('Add')}}</button> <button id="domain_deny_submit" class="btn btn-default">{{_('Add')}}</button>
</form> </form>
</div>
{% endif %} {% endif %}
</div> </div>
......
{% extends "layout.html" %}
{% block body %}
<h1 class="{{page}}">{{_(title)}}</h1>
<div class="filterheader hidden-xs hidden-sm">
{% if entries.__len__() %}
{% if entries[0][0].sort %}
<button id="sort_name" class="btn btn-primary"><b>B,A <-> A B</b></button>
{% endif %}
{% endif %}
<button id="desc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet"></span></button>
<button id="asc" class="btn btn-primary"><span class="glyphicon glyphicon-sort-by-alphabet-alt"></span></button>
{% if charlist|length %}
<button id="all" class="btn btn-primary">{{_('All')}}</button>
{% endif %}
<div class="btn-group character" role="group">
{% for char in charlist%}
<button class="btn btn-primary char">{{char.char}}</button>
{% endfor %}
</div>
<button class="update-view btn btn-primary" href="#" data-target="series_view" data-view="list">List</button>
</div>
{% if entries[0] %}
<div id="list" class="row">
{% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book sortable" {% if entry[0].sort %}data-name="{{entry[0].series[0].name}}"{% endif %} data-id="{% if entry[0].series[0].name %}{{entry[0].series[0].name}}{% endif %}">
<div class="cover">
<a href="{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].series[0].id )}}">
<img src="{{ url_for('web.get_cover', book_id=entry[0].id) }}" alt="{{ entry[0].name }}"/>
<span class="badge">{{entry.count}}</span>
</a>
</div>
<div class="meta">
<a href="{{url_for('web.books_list', data=data, sort='new', book_id=entry[0].series[0].id )}}">
<p class="title">{{entry[0].series[0].name|shortentitle}}</p>
</a>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}
{% block js %}
<script src="{{ url_for('static', filename='js/filter_grid.js') }}"></script>
{% endblock %}
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
<link href="{{ url_for('static', filename='css/upload.css') }}" rel="stylesheet" media="screen"> <link href="{{ url_for('static', filename='css/upload.css') }}" rel="stylesheet" media="screen">
{% if g.current_theme == 1 %} {% if g.current_theme == 1 %}
<link href="{{ url_for('static', filename='css/caliBlur.min.css') }}" rel="stylesheet" media="screen"> <link href="{{ url_for('static', filename='css/caliBlur.min.css') }}" rel="stylesheet" media="screen">
<link href="{{ url_for('static', filename='css/caliBlur_override.css') }}" rel="stylesheet" media="screen">
{% endif %} {% endif %}
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
...@@ -25,7 +26,7 @@ ...@@ -25,7 +26,7 @@
<script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.3.0/respond.min.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body class="{{ page }}" data-text="{{_('Home')}}" data-textback="{{_('Back')}}"> <body class="{{ page }} {{ bodyClass }}" data-text="{{_('Home')}}" data-textback="{{_('Back')}}">
<!-- Static navbar --> <!-- Static navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation"> <div class="navbar navbar-default navbar-static-top" role="navigation">
<div class="container-fluid"> <div class="container-fluid">
......
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
<button class="btn btn-primary char">{{char.char}}</button> <button class="btn btn-primary char">{{char.char}}</button>
{% endfor %} {% endfor %}
</div> </div>
{% if data == "series" %}
<button class="update-view btn btn-primary" href="#" data-target="series_view" data-view="grid">Grid</button>
{% endif %}
</div> </div>
<div class="container"> <div class="container">
<div id="list" class="col-xs-12 col-sm-6"> <div id="list" class="col-xs-12 col-sm-6">
......
...@@ -3,10 +3,12 @@ ...@@ -3,10 +3,12 @@
<div class="well col-sm-6 col-sm-offset-2"> <div class="well col-sm-6 col-sm-offset-2">
<h2 style="margin-top: 0">{{_('Register New Account')}}</h2> <h2 style="margin-top: 0">{{_('Register New Account')}}</h2>
<form method="POST" role="form"> <form method="POST" role="form">
{% if not config.config_register_email %}
<div class="form-group required"> <div class="form-group required">
<label for="nickname">{{_('Username')}}</label> <label for="nickname">{{_('Username')}}</label>
<input type="text" class="form-control" id="nickname" name="nickname" placeholder="{{_('Choose a username')}}" required> <input type="text" class="form-control" id="nickname" name="nickname" placeholder="{{_('Choose a username')}}" required>
</div> </div>
{% endif %}
<div class="form-group required"> <div class="form-group required">
<label for="email">{{_('E-mail Address')}}</label> <label for="email">{{_('E-mail Address')}}</label>
<input type="email" class="form-control" id="email" name="email" placeholder="{{_('Your email address')}}" required> <input type="email" class="form-control" id="email" name="email" placeholder="{{_('Your email address')}}" required>
......
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
<div class="col-sm-8"> <div class="col-md-10 col-lg-6">
<form role="form" id="search" action="{{ url_for('web.advanced_search') }}" method="GET"> <form role="form" id="search" action="{{ url_for('web.advanced_search') }}" method="GET">
<div class="form-group"> <div class="form-group">
<label for="book_title">{{_('Book Title')}}</label> <label for="book_title">{{_('Book Title')}}</label>
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<div class="discover"> <div class="discover">
<h1>{{title}}</h1> <h1>{{title}}</h1>
<form role="form" method="POST" autocomplete="off"> <form role="form" method="POST" autocomplete="off">
<div class="col-md-10 col-lg-8">
{% if new_user or ( g.user and content.nickname != "Guest" and g.user.role_admin() ) %} {% if new_user or ( g.user and content.nickname != "Guest" and g.user.role_admin() ) %}
<div class="form-group required"> <div class="form-group required">
<label for="nickname">{{_('Username')}}</label> <label for="nickname">{{_('Username')}}</label>
...@@ -65,6 +66,7 @@ ...@@ -65,6 +66,7 @@
<div class="btn btn-danger" id="config_delete_kobo_token" data-toggle="modal" data-target="#modalDeleteToken" data-remote="false" {% if not content.remote_auth_token.first() %} style="display: none;" {% endif %}>{{_('Delete')}}</div> <div class="btn btn-danger" id="config_delete_kobo_token" data-toggle="modal" data-target="#modalDeleteToken" data-remote="false" {% if not content.remote_auth_token.first() %} style="display: none;" {% endif %}>{{_('Delete')}}</div>
</div> </div>
{% endif %} {% endif %}
</div>
<div class="col-sm-6"> <div class="col-sm-6">
{% for element in sidebar %} {% for element in sidebar %}
{% if element['config_show'] %} {% if element['config_show'] %}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -49,6 +49,7 @@ from . import constants ...@@ -49,6 +49,7 @@ from . import constants
session = None session = None
app_DB_path = None
Base = declarative_base() Base = declarative_base()
...@@ -107,6 +108,11 @@ def get_sidebar_config(kwargs=None): ...@@ -107,6 +108,11 @@ def get_sidebar_config(kwargs=None):
{"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived", {"glyph": "glyphicon-trash", "text": _('Archived Books'), "link": 'web.books_list', "id": "archived",
"visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived", "visibility": constants.SIDEBAR_ARCHIVED, 'public': (not g.user.is_anonymous), "page": "archived",
"show_text": _('Show archived books'), "config_show": content}) "show_text": _('Show archived books'), "config_show": content})
'''sidebar.append(
{"glyph": "glyphicon-th-list", "text": _('Books List'), "link": 'web.books_list', "id": "list",
"visibility": constants.SIDEBAR_LIST, 'public': (not g.user.is_anonymous), "page": "list",
"show_text": _('Show Books List'), "config_show": content})'''
return sidebar return sidebar
...@@ -211,6 +217,7 @@ class User(UserBase, Base): ...@@ -211,6 +217,7 @@ class User(UserBase, Base):
denied_column_value = Column(String, default="") denied_column_value = Column(String, default="")
allowed_column_value = Column(String, default="") allowed_column_value = Column(String, default="")
remote_auth_token = relationship('RemoteAuthToken', backref='user', lazy='dynamic') remote_auth_token = relationship('RemoteAuthToken', backref='user', lazy='dynamic')
series_view = Column(String(10), default="list")
if oauth_support: if oauth_support:
...@@ -251,6 +258,7 @@ class Anonymous(AnonymousUserMixin, UserBase): ...@@ -251,6 +258,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
self.allowed_tags = data.allowed_tags self.allowed_tags = data.allowed_tags
self.denied_column_value = data.denied_column_value self.denied_column_value = data.denied_column_value
self.allowed_column_value = data.allowed_column_value self.allowed_column_value = data.allowed_column_value
self.series_view = data.series_view
def role_admin(self): def role_admin(self):
return False return False
...@@ -447,7 +455,7 @@ class RemoteAuthToken(Base): ...@@ -447,7 +455,7 @@ class RemoteAuthToken(Base):
# Migrate database to current version, has to be updated after every database change. Currently migration from # 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 # everywhere to current should work. Migration is done by checking if relevant columns are existing, and than adding
# rows with SQL commands # rows with SQL commands
def migrate_Database(session): def migrate_Database(session):
engine = session.bind engine = session.bind
...@@ -557,6 +565,12 @@ def migrate_Database(session): ...@@ -557,6 +565,12 @@ def migrate_Database(session):
conn.execute("ALTER TABLE user ADD column `denied_column_value` DEFAULT ''") conn.execute("ALTER TABLE user ADD column `denied_column_value` DEFAULT ''")
conn.execute("ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''") conn.execute("ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''")
session.commit() session.commit()
try:
session.query(exists().where(User.series_view)).scalar()
except exc.OperationalError:
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `series_view` VARCHAR(10) DEFAULT 'list'")
if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \ if session.query(User).filter(User.role.op('&')(constants.ROLE_ANONYMOUS) == constants.ROLE_ANONYMOUS).first() \
is None: is None:
create_anonymous_user(session) create_anonymous_user(session)
...@@ -568,7 +582,7 @@ def migrate_Database(session): ...@@ -568,7 +582,7 @@ def migrate_Database(session):
# Create new table user_id and copy contents of table user into it # Create new table user_id and copy contents of table user into it
conn = engine.connect() conn = engine.connect()
conn.execute("CREATE TABLE user_id (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," conn.execute("CREATE TABLE user_id (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
" nickname VARCHAR(64)," "nickname VARCHAR(64),"
"email VARCHAR(120)," "email VARCHAR(120),"
"role SMALLINT," "role SMALLINT,"
"password VARCHAR," "password VARCHAR,"
...@@ -576,10 +590,11 @@ def migrate_Database(session): ...@@ -576,10 +590,11 @@ def migrate_Database(session):
"locale VARCHAR(2)," "locale VARCHAR(2),"
"sidebar_view INTEGER," "sidebar_view INTEGER,"
"default_language VARCHAR(3)," "default_language VARCHAR(3),"
"series_view VARCHAR(10),"
"UNIQUE (nickname)," "UNIQUE (nickname),"
"UNIQUE (email))") "UNIQUE (email))")
conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale," conn.execute("INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
"sidebar_view, default_language) " "sidebar_view, default_language, series_view) "
"SELECT id, nickname, email, role, password, kindle_mail, locale," "SELECT id, nickname, email, role, password, kindle_mail, locale,"
"sidebar_view, default_language FROM user") "sidebar_view, default_language FROM user")
# delete old user table and rename new user_id table to user: # delete old user table and rename new user_id table to user:
...@@ -616,8 +631,7 @@ def delete_download(book_id): ...@@ -616,8 +631,7 @@ def delete_download(book_id):
session.query(Downloads).filter(book_id == Downloads.book_id).delete() session.query(Downloads).filter(book_id == Downloads.book_id).delete()
session.commit() session.commit()
# Generate user Guest (translated text), as anonymous user, no rights
# Generate user Guest (translated text), as anoymous user, no rights
def create_anonymous_user(session): def create_anonymous_user(session):
user = User() user = User()
user.nickname = "Guest" user.nickname = "Guest"
...@@ -651,7 +665,9 @@ def create_admin_user(session): ...@@ -651,7 +665,9 @@ def create_admin_user(session):
def init_db(app_db_path): def init_db(app_db_path):
# Open session for database connection # Open session for database connection
global session global session
global app_DB_path
app_DB_path = app_db_path
engine = create_engine(u'sqlite:///{0}'.format(app_db_path), echo=False) engine = create_engine(u'sqlite:///{0}'.format(app_db_path), echo=False)
Session = sessionmaker() Session = sessionmaker()
......
...@@ -40,7 +40,7 @@ try: ...@@ -40,7 +40,7 @@ try:
from wand.exceptions import PolicyError from wand.exceptions import PolicyError
use_generic_pdf_cover = False use_generic_pdf_cover = False
except (ImportError, RuntimeError) as e: except (ImportError, RuntimeError) as e:
log.debug('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e) log.debug('Cannot import Image, generating pdf covers for pdf uploads will not work: %s', e)
use_generic_pdf_cover = True use_generic_pdf_cover = True
try: try:
...@@ -48,21 +48,21 @@ try: ...@@ -48,21 +48,21 @@ try:
from PyPDF2 import __version__ as PyPdfVersion from PyPDF2 import __version__ as PyPdfVersion
use_pdf_meta = True use_pdf_meta = True
except ImportError as e: except ImportError as e:
log.debug('cannot import PyPDF2, extracting pdf metadata will not work: %s', e) log.debug('Cannot import PyPDF2, extracting pdf metadata will not work: %s', e)
use_pdf_meta = False use_pdf_meta = False
try: try:
from . import epub from . import epub
use_epub_meta = True use_epub_meta = True
except ImportError as e: except ImportError as e:
log.debug('cannot import epub, extracting epub metadata will not work: %s', e) log.debug('Cannot import epub, extracting epub metadata will not work: %s', e)
use_epub_meta = False use_epub_meta = False
try: try:
from . import fb2 from . import fb2
use_fb2_meta = True use_fb2_meta = True
except ImportError as e: except ImportError as e:
log.debug('cannot import fb2, extracting fb2 metadata will not work: %s', e) log.debug('Cannot import fb2, extracting fb2 metadata will not work: %s', e)
use_fb2_meta = False use_fb2_meta = False
try: try:
...@@ -70,20 +70,17 @@ try: ...@@ -70,20 +70,17 @@ try:
from PIL import __version__ as PILversion from PIL import __version__ as PILversion
use_PIL = True use_PIL = True
except ImportError as e: except ImportError as e:
log.debug('cannot import Pillow, using png and webp images as cover will not work: %s', e) log.debug('Cannot import Pillow, using png and webp images as cover will not work: %s', e)
use_PIL = False use_PIL = False
__author__ = 'lemmsh'
def process(tmp_file_path, original_file_name, original_file_extension, rarExecutable): def process(tmp_file_path, original_file_name, original_file_extension, rarExecutable):
"""Get the metadata for tmp_file_path."""
meta = None meta = None
extension_upper = original_file_extension.upper() extension_upper = original_file_extension.upper()
try: try:
if ".PDF" == extension_upper: if ".PDF" == extension_upper:
meta = pdf_meta(tmp_file_path, original_file_name, original_file_extension) meta = pdf_meta(tmp_file_path, original_file_name, original_file_extension)
elif ".EPUB" == extension_upper and use_epub_meta is True: elif extension_upper in [".KEPUB", ".EPUB"] and use_epub_meta is True:
meta = epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension) meta = epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension)
elif ".FB2" == extension_upper and use_fb2_meta is True: elif ".FB2" == extension_upper and use_fb2_meta is True:
meta = fb2.get_fb2_info(tmp_file_path, original_file_extension) meta = fb2.get_fb2_info(tmp_file_path, original_file_extension)
...@@ -182,7 +179,7 @@ def get_versions(): ...@@ -182,7 +179,7 @@ def get_versions():
else: else:
PILVersion = u'not installed' PILVersion = u'not installed'
if comic.use_comic_meta: if comic.use_comic_meta:
ComicVersion = u'installed' ComicVersion = comic.comic_version or u'installed'
else: else:
ComicVersion = u'not installed' ComicVersion = u'not installed'
return {'Image Magick': IVersion, return {'Image Magick': IVersion,
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -31,7 +31,7 @@ rarfile>=2.7 ...@@ -31,7 +31,7 @@ rarfile>=2.7
# other # other
natsort>=2.2.0,<7.1.0 natsort>=2.2.0,<7.1.0
git+https://github.com/OzzieIsaacs/comicapi.git@15dff9ce4e1ffed29ba4a2feadfcdb6bed00bcad#egg=comicapi git+https://github.com/OzzieIsaacs/comicapi.git@3e15b950b72724b1b8ca619c36580b5fbaba9784#egg=comicapi
#Kobo integration #Kobo integration
jsonschema>=3.2.0,<3.3.0 jsonschema>=3.2.0,<3.3.0
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment