Commit b852fb0e authored by Ozzieisaacs's avatar Ozzieisaacs

Merge branch 'Develop'

parents 087c4c59 0fc18bf3
...@@ -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 title == "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'] %}
......
...@@ -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.
...@@ -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