Commit ba44a158 authored by OzzieIsaacs's avatar OzzieIsaacs

changes for #77

Code cosmetics
#75:
- More debug infos for kindlegen and sending e-mail.
- Button for sending test e-mail.
- timeout of 5min for sending e-mail
parent c582ccf7
...@@ -17,6 +17,7 @@ eggs/ ...@@ -17,6 +17,7 @@ eggs/
*.db *.db
*.log *.log
config.ini config.ini
cps/static/[0-9]*
.idea/ .idea/
*.bak *.bak
......
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
import os import os
import sys import sys
from threading import Thread
from multiprocessing import Queue
import time
base_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.dirname(os.path.abspath(__file__))
# Insert local directories into path # Insert local directories into path
sys.path.insert(0,os.path.join(base_path, 'vendor')) sys.path.insert(0, os.path.join(base_path, 'vendor'))
from cps import web from cps import web
from cps import config from cps import config
...@@ -15,11 +19,47 @@ from tornado.ioloop import IOLoop ...@@ -15,11 +19,47 @@ from tornado.ioloop import IOLoop
global title_sort global title_sort
def title_sort(title): def title_sort(title):
return title return title
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0",port=config.PORT, debug=True)
else: def start_calibreweb(messagequeue):
web.global_queue = messagequeue
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0", port=config.PORT, debug=True)
else:
http_server = HTTPServer(WSGIContainer(web.app)) http_server = HTTPServer(WSGIContainer(web.app))
http_server.listen(config.PORT) http_server.listen(config.PORT)
IOLoop.instance().start() IOLoop.instance().start()
print "Tornado finished"
http_server.stop()
def stop_calibreweb():
# Close Database connections for user and data
web.db.session.close()
web.db.engine.dispose()
web.ub.session.close()
web.ub.engine.dispose()
test=IOLoop.instance()
test.add_callback(test.stop)
print("Asked Tornado to exit")
if __name__ == '__main__':
if config.DEVELOPMENT:
web.app.run(host="0.0.0.0",port=config.PORT, debug=True)
else:
while True:
q = Queue()
t = Thread(target=start_calibreweb, args=(q,))
t.start()
while True: #watching queue, if there is no call than sleep, otherwise break
if q.empty():
time.sleep(1)
else:
break
stop_calibreweb()
t.join()
import logging
import uploader
import os
from flask_babel import gettext as _
__author__ = 'lemmsh' __author__ = 'lemmsh'
import logging
logger = logging.getLogger("book_formats") logger = logging.getLogger("book_formats")
import uploader
import os
try: try:
from wand.image import Image from wand.image import Image
from wand import version as ImageVersion
use_generic_pdf_cover = False use_generic_pdf_cover = False
except ImportError, e: except ImportError, e:
logger.warning('cannot import Image, generating pdf covers for pdf uploads will not work: %s', e) logger.warning('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:
from PyPDF2 import PdfFileReader from PyPDF2 import PdfFileReader
from PyPDF2 import __version__ as PyPdfVersion
use_pdf_meta = True use_pdf_meta = True
except ImportError, e: except ImportError, e:
logger.warning('cannot import PyPDF2, extracting pdf metadata will not work: %s', e) logger.warning('cannot import PyPDF2, extracting pdf metadata will not work: %s', e)
...@@ -37,9 +41,9 @@ def process(tmp_file_path, original_file_name, original_file_extension): ...@@ -37,9 +41,9 @@ def process(tmp_file_path, original_file_name, original_file_extension):
try: try:
if ".PDF" == original_file_extension.upper(): if ".PDF" == original_file_extension.upper():
return pdf_meta(tmp_file_path, original_file_name, original_file_extension) return pdf_meta(tmp_file_path, original_file_name, original_file_extension)
if ".EPUB" == original_file_extension.upper() and use_epub_meta == True: if ".EPUB" == original_file_extension.upper() and use_epub_meta is True:
return epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension) return epub.get_epub_info(tmp_file_path, original_file_name, original_file_extension)
if ".FB2" == original_file_extension.upper() and use_fb2_meta == True: if ".FB2" == original_file_extension.upper() and use_fb2_meta is True:
return fb2.get_fb2_info(tmp_file_path, original_file_name, original_file_extension) return fb2.get_fb2_info(tmp_file_path, original_file_name, original_file_extension)
except Exception, e: except Exception, e:
logger.warning('cannot parse metadata, using default: %s', e) logger.warning('cannot parse metadata, using default: %s', e)
...@@ -47,29 +51,28 @@ def process(tmp_file_path, original_file_name, original_file_extension): ...@@ -47,29 +51,28 @@ def process(tmp_file_path, original_file_name, original_file_extension):
return default_meta(tmp_file_path, original_file_name, original_file_extension) return default_meta(tmp_file_path, original_file_name, original_file_extension)
def default_meta(tmp_file_path, original_file_name, original_file_extension): def default_meta(tmp_file_path, original_file_name, original_file_extension):
return uploader.BookMeta( return uploader.BookMeta(
file_path = tmp_file_path, file_path=tmp_file_path,
extension = original_file_extension, extension=original_file_extension,
title = original_file_name, title=original_file_name,
author = "Unknown", author="Unknown",
cover = None, cover=None,
description = "", description="",
tags = "", tags="",
series = "", series="",
series_id="") series_id="")
def pdf_meta(tmp_file_path, original_file_name, original_file_extension): def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
if (use_pdf_meta): if use_pdf_meta:
pdf = PdfFileReader(open(tmp_file_path, 'rb')) pdf = PdfFileReader(open(tmp_file_path, 'rb'))
doc_info = pdf.getDocumentInfo() doc_info = pdf.getDocumentInfo()
else: else:
doc_info = None doc_info = None
if (doc_info is not None): if doc_info is not None:
author = doc_info.author if doc_info.author is not None else "Unknown" author = doc_info.author if doc_info.author is not None else "Unknown"
title = doc_info.title if doc_info.title is not None else original_file_name title = doc_info.title if doc_info.title is not None else original_file_name
subject = doc_info.subject subject = doc_info.subject
...@@ -78,16 +81,17 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension): ...@@ -78,16 +81,17 @@ def pdf_meta(tmp_file_path, original_file_name, original_file_extension):
title = original_file_name title = original_file_name
subject = "" subject = ""
return uploader.BookMeta( return uploader.BookMeta(
file_path = tmp_file_path, file_path=tmp_file_path,
extension = original_file_extension, extension=original_file_extension,
title = title, title=title,
author = author, author=author,
cover = pdf_preview(tmp_file_path, original_file_name), cover=pdf_preview(tmp_file_path, original_file_name),
description = subject, description=subject,
tags = "", tags="",
series = "", series="",
series_id="") series_id="")
def pdf_preview(tmp_file_path, tmp_dir): def pdf_preview(tmp_file_path, tmp_dir):
if use_generic_pdf_cover: if use_generic_pdf_cover:
return None return None
...@@ -97,3 +101,14 @@ def pdf_preview(tmp_file_path, tmp_dir): ...@@ -97,3 +101,14 @@ def pdf_preview(tmp_file_path, tmp_dir):
img.compression_quality = 88 img.compression_quality = 88
img.save(filename=os.path.join(tmp_dir, cover_file_name)) img.save(filename=os.path.join(tmp_dir, cover_file_name))
return cover_file_name return cover_file_name
def get_versions():
if not use_generic_pdf_cover:
IVersion=ImageVersion.MAGICK_VERSION
else:
IVersion=_('not installed')
if use_pdf_meta:
PVersion=PyPdfVersion
else:
PVersion=_('not installed')
return {'ImageVersion':IVersion,'PyPdfVersion':PVersion}
\ No newline at end of file
...@@ -5,9 +5,10 @@ import os ...@@ -5,9 +5,10 @@ import os
import sys import sys
from configobj import ConfigObj from configobj import ConfigObj
CONFIG_FILE= os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "config.ini") CONFIG_FILE = os.path.join(os.path.normpath(os.path.dirname(os.path.realpath(__file__))+os.sep+".."+os.sep), "config.ini")
CFG = ConfigObj(CONFIG_FILE) CFG = ConfigObj(CONFIG_FILE)
CFG.encoding='UTF-8' CFG.encoding = 'UTF-8'
def CheckSection(sec): def CheckSection(sec):
""" Check if INI section exists, if not create it """ """ Check if INI section exists, if not create it """
...@@ -18,6 +19,7 @@ def CheckSection(sec): ...@@ -18,6 +19,7 @@ def CheckSection(sec):
CFG[sec] = {} CFG[sec] = {}
return False return False
def check_setting_str(config, cfg_name, item_name, def_val, log=True): def check_setting_str(config, cfg_name, item_name, def_val, log=True):
try: try:
my_val = config[cfg_name][item_name].decode('UTF-8') my_val = config[cfg_name][item_name].decode('UTF-8')
...@@ -62,24 +64,16 @@ PUBLIC_REG = bool(check_setting_int(CFG, 'Advanced', 'PUBLIC_REG', 0)) ...@@ -62,24 +64,16 @@ PUBLIC_REG = bool(check_setting_int(CFG, 'Advanced', 'PUBLIC_REG', 0))
UPLOADING = bool(check_setting_int(CFG, 'Advanced', 'UPLOADING', 0)) UPLOADING = bool(check_setting_int(CFG, 'Advanced', 'UPLOADING', 0))
ANON_BROWSE = bool(check_setting_int(CFG, 'Advanced', 'ANON_BROWSE', 0)) ANON_BROWSE = bool(check_setting_int(CFG, 'Advanced', 'ANON_BROWSE', 0))
SYS_ENCODING="UTF-8" SYS_ENCODING = "UTF-8"
if DB_ROOT == "": if DB_ROOT == "":
print "Calibre database directory (DB_ROOT) is not configured" print "Calibre database directory (DB_ROOT) is not configured"
sys.exit(1) sys.exit(1)
configval={} configval = {"DB_ROOT": DB_ROOT, "APP_DB_ROOT": APP_DB_ROOT, "MAIN_DIR": MAIN_DIR, "LOG_DIR": LOG_DIR, "PORT": PORT,
configval["DB_ROOT"] = DB_ROOT "NEWEST_BOOKS": NEWEST_BOOKS, "DEVELOPMENT": DEVELOPMENT, "TITLE_REGEX": TITLE_REGEX,
configval["APP_DB_ROOT"] = APP_DB_ROOT "PUBLIC_REG": PUBLIC_REG, "UPLOADING": UPLOADING, "ANON_BROWSE": ANON_BROWSE}
configval["MAIN_DIR"] = MAIN_DIR
configval["LOG_DIR"] = LOG_DIR
configval["PORT"] = PORT
configval["NEWEST_BOOKS"] = NEWEST_BOOKS
configval["DEVELOPMENT"] = DEVELOPMENT
configval["TITLE_REGEX"] = TITLE_REGEX
configval["PUBLIC_REG"] = PUBLIC_REG
configval["UPLOADING"] = UPLOADING
configval["ANON_BROWSE"] = ANON_BROWSE
def save_config(configval): def save_config(configval):
new_config = ConfigObj(encoding='UTF-8') new_config = ConfigObj(encoding='UTF-8')
......
...@@ -9,8 +9,10 @@ import config ...@@ -9,8 +9,10 @@ import config
import re import re
import ast import ast
#calibre sort stuff # calibre sort stuff
title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE) title_pat = re.compile(config.TITLE_REGEX, re.IGNORECASE)
def title_sort(title): def title_sort(title):
match = title_pat.search(title) match = title_pat.search(title)
if match: if match:
...@@ -52,7 +54,7 @@ books_languages_link = Table('books_languages_link', Base.metadata, ...@@ -52,7 +54,7 @@ books_languages_link = Table('books_languages_link', Base.metadata,
cc = conn.execute("SELECT id, datatype FROM custom_columns") cc = conn.execute("SELECT id, datatype FROM custom_columns")
cc_ids = [] cc_ids = []
cc_exceptions = [ 'datetime', 'int', 'comments', 'float', 'composite','series' ] cc_exceptions = ['datetime', 'int', 'comments', 'float', 'composite', 'series']
books_custom_column_links = {} books_custom_column_links = {}
cc_classes = {} cc_classes = {}
for row in cc: for row in cc:
...@@ -61,18 +63,19 @@ for row in cc: ...@@ -61,18 +63,19 @@ for row in cc:
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('value', Integer, ForeignKey('custom_column_' + str(row.id) + '.id'), primary_key=True) Column('value', Integer, ForeignKey('custom_column_' + str(row.id) + '.id'), primary_key=True)
) )
cc_ids.append([row.id,row.datatype]) cc_ids.append([row.id, row.datatype])
if row.datatype == 'bool': if row.datatype == 'bool':
ccdict = {'__tablename__': 'custom_column_' + str(row.id), ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id': Column(Integer, primary_key=True), 'id': Column(Integer, primary_key=True),
'book': Column(Integer,ForeignKey('books.id')), 'book': Column(Integer, ForeignKey('books.id')),
'value': Column(Boolean)} 'value': Column(Boolean)}
else: else:
ccdict={'__tablename__':'custom_column_' + str(row.id), ccdict = {'__tablename__': 'custom_column_' + str(row.id),
'id':Column(Integer, primary_key=True), 'id': Column(Integer, primary_key=True),
'value':Column(String)} 'value': Column(String)}
cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict) cc_classes[row.id] = type('Custom_Column_' + str(row.id), (Base,), ccdict)
class Comments(Base): class Comments(Base):
__tablename__ = 'comments' __tablename__ = 'comments'
...@@ -100,6 +103,7 @@ class Tags(Base): ...@@ -100,6 +103,7 @@ class Tags(Base):
def __repr__(self): def __repr__(self):
return u"<Tags('{0})>".format(self.name) return u"<Tags('{0})>".format(self.name)
class Authors(Base): class Authors(Base):
__tablename__ = 'authors' __tablename__ = 'authors'
...@@ -116,6 +120,7 @@ class Authors(Base): ...@@ -116,6 +120,7 @@ class Authors(Base):
def __repr__(self): def __repr__(self):
return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link) return u"<Authors('{0},{1}{2}')>".format(self.name, self.sort, self.link)
class Series(Base): class Series(Base):
__tablename__ = 'series' __tablename__ = 'series'
...@@ -130,30 +135,33 @@ class Series(Base): ...@@ -130,30 +135,33 @@ class Series(Base):
def __repr__(self): def __repr__(self):
return u"<Series('{0},{1}')>".format(self.name, self.sort) return u"<Series('{0},{1}')>".format(self.name, self.sort)
class Ratings(Base): class Ratings(Base):
__tablename__ = 'ratings' __tablename__ = 'ratings'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
rating = Column(Integer) rating = Column(Integer)
def __init__(self,rating): def __init__(self, rating):
self.rating = rating self.rating = rating
def __repr__(self): def __repr__(self):
return u"<Ratings('{0}')>".format(self.rating) return u"<Ratings('{0}')>".format(self.rating)
class Languages(Base): class Languages(Base):
__tablename__ = 'languages' __tablename__ = 'languages'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
lang_code = Column(String) lang_code = Column(String)
def __init__(self,lang_code): def __init__(self, lang_code):
self.lang_code = lang_code self.lang_code = lang_code
def __repr__(self): def __repr__(self):
return u"<Languages('{0}')>".format(self.lang_code) return u"<Languages('{0}')>".format(self.lang_code)
class Data(Base): class Data(Base):
__tablename__ = 'data' __tablename__ = 'data'
...@@ -172,6 +180,7 @@ class Data(Base): ...@@ -172,6 +180,7 @@ class Data(Base):
def __repr__(self): def __repr__(self):
return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name) return u"<Data('{0},{1}{2}{3}')>".format(self.book, self.format, self.uncompressed_size, self.name)
class Books(Base): class Books(Base):
__tablename__ = 'books' __tablename__ = 'books'
...@@ -207,17 +216,24 @@ class Books(Base): ...@@ -207,17 +216,24 @@ class Books(Base):
self.has_cover = has_cover self.has_cover = has_cover
def __repr__(self): def __repr__(self):
return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort, self.timestamp, self.pubdate, self.series_index, self.last_modified ,self.path, self.has_cover) return u"<Books('{0},{1}{2}{3}{4}{5}{6}{7}{8}')>".format(self.title, self.sort, self.author_sort,
self.timestamp, self.pubdate, self.series_index,
self.last_modified, self.path, self.has_cover)
for id in cc_ids: for id in cc_ids:
if id[1] == 'bool': if id[1] == 'bool':
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], primaryjoin=(Books.id==cc_classes[id[0]].book), backref='books')) setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
primaryjoin=(Books.id == cc_classes[id[0]].book),
backref='books'))
else: else:
setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]], secondary=books_custom_column_links[id[0]], backref='books')) setattr(Books, 'custom_column_' + str(id[0]), relationship(cc_classes[id[0]],
secondary = books_custom_column_links[id[0]],
backref='books'))
class Custom_Columns(Base): class Custom_Columns(Base):
__tablename__ = 'custom_columns' __tablename__ = 'custom_columns'
id = Column(Integer,primary_key=True) id = Column(Integer, primary_key=True)
label = Column(String) label = Column(String)
name = Column(String) name = Column(String)
datatype = Column(String) datatype = Column(String)
...@@ -231,9 +247,7 @@ class Custom_Columns(Base): ...@@ -231,9 +247,7 @@ class Custom_Columns(Base):
display_dict = ast.literal_eval(self.display) display_dict = ast.literal_eval(self.display)
return display_dict return display_dict
#Base.metadata.create_all(engine) # Base.metadata.create_all(engine)
Session = sessionmaker() Session = sessionmaker()
Session.configure(bind=engine) Session.configure(bind=engine)
session = Session() session = Session()
...@@ -3,8 +3,9 @@ from lxml import etree ...@@ -3,8 +3,9 @@ from lxml import etree
import os import os
import uploader import uploader
def extractCover(zip, coverFile, tmp_file_name): def extractCover(zip, coverFile, tmp_file_name):
if (coverFile is None): if coverFile is None:
return None return None
else: else:
cf = zip.read("OPS/" + coverFile) cf = zip.read("OPS/" + coverFile)
...@@ -16,35 +17,34 @@ def extractCover(zip, coverFile, tmp_file_name): ...@@ -16,35 +17,34 @@ def extractCover(zip, coverFile, tmp_file_name):
return tmp_cover_name return tmp_cover_name
def get_epub_info(tmp_file_path, original_file_name, original_file_extension): def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
ns = { ns = {
'n':'urn:oasis:names:tc:opendocument:xmlns:container', 'n': 'urn:oasis:names:tc:opendocument:xmlns:container',
'pkg':'http://www.idpf.org/2007/opf', 'pkg': 'http://www.idpf.org/2007/opf',
'dc':'http://purl.org/dc/elements/1.1/' 'dc': 'http://purl.org/dc/elements/1.1/'
} }
zip = zipfile.ZipFile(tmp_file_path) zip = zipfile.ZipFile(tmp_file_path)
txt = zip.read('META-INF/container.xml') txt = zip.read('META-INF/container.xml')
tree = etree.fromstring(txt) tree = etree.fromstring(txt)
cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path',namespaces=ns)[0] cfname = tree.xpath('n:rootfiles/n:rootfile/@full-path', namespaces=ns)[0]
cf = zip.read(cfname) cf = zip.read(cfname)
tree = etree.fromstring(cf) tree = etree.fromstring(cf)
p = tree.xpath('/pkg:package/pkg:metadata',namespaces=ns)[0] p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0]
epub_metadata = {} epub_metadata = {}
for s in ['title', 'description', 'creator']: for s in ['title', 'description', 'creator']:
tmp = p.xpath('dc:%s/text()'%(s),namespaces=ns) tmp = p.xpath('dc:%s/text()' % s, namespaces=ns)
if (len(tmp) > 0): if len(tmp) > 0:
epub_metadata[s] = p.xpath('dc:%s/text()'%(s),namespaces=ns)[0] epub_metadata[s] = p.xpath('dc:%s/text()' % s, namespaces=ns)[0]
else: else:
epub_metadata[s] = "Unknown" epub_metadata[s] = "Unknown"
coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href",namespaces=ns) coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns)
if (len(coversection) > 0): if len(coversection) > 0:
coverfile = extractCover(zip, coversection[0], tmp_file_path) coverfile = extractCover(zip, coversection[0], tmp_file_path)
else: else:
coverfile = None coverfile = None
...@@ -53,15 +53,13 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension): ...@@ -53,15 +53,13 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
else: else:
title = epub_metadata['title'] title = epub_metadata['title']
return uploader.BookMeta( return uploader.BookMeta(
file_path = tmp_file_path, file_path=tmp_file_path,
extension = original_file_extension, extension=original_file_extension,
title = title, title=title,
author = epub_metadata['creator'], author=epub_metadata['creator'],
cover = coverfile, cover=coverfile,
description = epub_metadata['description'], description=epub_metadata['description'],
tags = "", tags="",
series = "", series="",
series_id="") series_id="")
...@@ -7,29 +7,31 @@ import uploader ...@@ -7,29 +7,31 @@ import uploader
def get_fb2_info(tmp_file_path, original_file_name, original_file_extension): def get_fb2_info(tmp_file_path, original_file_name, original_file_extension):
ns = { ns = {
'fb':'http://www.gribuser.ru/xml/fictionbook/2.0', 'fb': 'http://www.gribuser.ru/xml/fictionbook/2.0',
'l':'http://www.w3.org/1999/xlink', 'l': 'http://www.w3.org/1999/xlink',
} }
fb2_file = open(tmp_file_path) fb2_file = open(tmp_file_path)
tree = etree.fromstring(fb2_file.read()) tree = etree.fromstring(fb2_file.read())
authors = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:author', namespaces=ns) authors = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:author', namespaces=ns)
def get_author(element): def get_author(element):
return element.xpath('fb:first-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:middle-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:last-name/text()', namespaces=ns)[0] return element.xpath('fb:first-name/text()', namespaces=ns)[0] + ' ' + element.xpath('fb:middle-name/text()',
namespaces=ns)[0] + ' ' + element.xpath('fb:last-name/text()', namespaces=ns)[0]
author = ", ".join(map(get_author, authors)) author = ", ".join(map(get_author, authors))
title = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns)[0]) title = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns)[0])
description = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()', namespaces=ns)[0]) description = unicode(tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()',
namespaces=ns)[0])
return uploader.BookMeta( return uploader.BookMeta(
file_path = tmp_file_path, file_path=tmp_file_path,
extension = original_file_extension, extension=original_file_extension,
title = title, title=title,
author = author, author=author,
cover = None, cover=None,
description = description, description=description,
tags = "", tags="",
series = "", series="",
series_id="") series_id="")
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
import db, ub import db, ub
import config import config
from flask import current_app as app from flask import current_app as app
import logging
import smtplib import smtplib
import tempfile
import socket import socket
import sys import sys
import os import os
...@@ -21,16 +23,19 @@ from email.generator import Generator ...@@ -21,16 +23,19 @@ from email.generator import Generator
from flask_babel import gettext as _ from flask_babel import gettext as _
import subprocess import subprocess
def update_download(book_id, user_id): def update_download(book_id, user_id):
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == book_id).first() check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
book_id).first()
if not check: if not check:
new_download = ub.Downloads(user_id=user_id, book_id=book_id) new_download = ub.Downloads(user_id=user_id, book_id=book_id)
ub.session.add(new_download) ub.session.add(new_download)
ub.session.commit() ub.session.commit()
def make_mobi(book_id): def make_mobi(book_id):
if sys.platform =="win32": if sys.platform == "win32":
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe") kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen.exe")
else: else:
kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen") kindlegen = os.path.join(config.MAIN_DIR, "vendor", u"kindlegen")
...@@ -45,9 +50,17 @@ def make_mobi(book_id): ...@@ -45,9 +50,17 @@ def make_mobi(book_id):
file_path = os.path.join(config.DB_ROOT, book.path, data.name) file_path = os.path.join(config.DB_ROOT, book.path, data.name)
if os.path.exists(file_path + u".epub"): if os.path.exists(file_path + u".epub"):
p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()), shell=True, stdout=subprocess.PIPE, p = subprocess.Popen((kindlegen + " \"" + file_path + u".epub\" ").encode(sys.getfilesystemencoding()),
stderr=subprocess.PIPE, stdin=subprocess.PIPE) shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
check = p.wait() # Poll process for new output until finished
while True:
nextline = p.stdout.readline()
if nextline == '' and p.poll() is not None:
break
if nextline != "\r\n":
app.logger.debug(nextline.strip('\r\n'))
check = p.returncode
if not check or check < 2: if not check or check < 2:
book.data.append(db.Data( book.data.append(db.Data(
name=book.data[0].name, name=book.data[0].name,
...@@ -64,8 +77,67 @@ def make_mobi(book_id): ...@@ -64,8 +77,67 @@ def make_mobi(book_id):
app.logger.error("make_mobie: epub not found: %s.epub" % file_path) app.logger.error("make_mobie: epub not found: %s.epub" % file_path)
return None return None
class StderrLogger(object):
buffer=''
def __init__(self):
self.logger = logging.getLogger('cps.web')
def write(self, message):
if message=='\n':
self.logger.debug(self.buffer)
self.buffer=''
else:
self.buffer=self.buffer+message
def send_test_mail(kindle_mail):
settings = ub.get_mail_settings()
msg = MIMEMultipart()
msg['From'] = settings["mail_from"]
msg['To'] = kindle_mail
msg['Subject'] = _('Calibre-web test email')
text = _('This email has been sent via calibre web.')
use_ssl = settings.get('mail_use_ssl', 0)
# convert MIME message to string
fp = StringIO()
gen = Generator(fp, mangle_from_=False)
gen.flatten(msg)
msg = fp.getvalue()
# send email
try:
timeout=600 # set timeout to 5mins
org_stderr = smtplib.stderr
smtplib.stderr = StderrLogger()
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"],timeout)
mailserver.set_debuglevel(1)
if int(use_ssl) == 1:
mailserver.ehlo()
mailserver.starttls()
mailserver.ehlo()
if settings["mail_password"]:
mailserver.login(settings["mail_login"], settings["mail_password"])
mailserver.sendmail(settings["mail_login"], kindle_mail, msg)
mailserver.quit()
smtplib.stderr = org_stderr
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
app.logger.error(traceback.print_exc())
return _("Failed to send mail: %s" % str(e))
return None
def send_mail(book_id, kindle_mail): def send_mail(book_id, kindle_mail):
'''Send email with attachments''' """Send email with attachments"""
is_mobi = False is_mobi = False
is_azw = False is_azw = False
is_azw3 = False is_azw3 = False
...@@ -84,7 +156,7 @@ def send_mail(book_id, kindle_mail): ...@@ -84,7 +156,7 @@ def send_mail(book_id, kindle_mail):
use_ssl = settings.get('mail_use_ssl', 0) use_ssl = settings.get('mail_use_ssl', 0)
# attach files # attach files
#msg.attach(self.get_attachment(file_path)) # msg.attach(self.get_attachment(file_path))
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
data = db.session.query(db.Data).filter(db.Data.book == book.id) data = db.session.query(db.Data).filter(db.Data.book == book.id)
...@@ -125,8 +197,13 @@ def send_mail(book_id, kindle_mail): ...@@ -125,8 +197,13 @@ def send_mail(book_id, kindle_mail):
# send email # send email
try: try:
mailserver = smtplib.SMTP(settings["mail_server"],settings["mail_port"]) timeout=600 # set timeout to 5mins
mailserver.set_debuglevel(0)
org_stderr = smtplib.stderr
smtplib.stderr = StderrLogger()
mailserver = smtplib.SMTP(settings["mail_server"], settings["mail_port"],timeout)
mailserver.set_debuglevel(1)
if int(use_ssl) == 1: if int(use_ssl) == 1:
mailserver.ehlo() mailserver.ehlo()
...@@ -137,6 +214,9 @@ def send_mail(book_id, kindle_mail): ...@@ -137,6 +214,9 @@ def send_mail(book_id, kindle_mail):
mailserver.login(settings["mail_login"], settings["mail_password"]) mailserver.login(settings["mail_login"], settings["mail_password"])
mailserver.sendmail(settings["mail_login"], kindle_mail, msg) mailserver.sendmail(settings["mail_login"], kindle_mail, msg)
mailserver.quit() mailserver.quit()
smtplib.stderr = org_stderr
except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e: except (socket.error, smtplib.SMTPRecipientsRefused, smtplib.SMTPException), e:
app.logger.error(traceback.print_exc()) app.logger.error(traceback.print_exc())
return _("Failed to send mail: %s" % str(e)) return _("Failed to send mail: %s" % str(e))
...@@ -145,7 +225,7 @@ def send_mail(book_id, kindle_mail): ...@@ -145,7 +225,7 @@ def send_mail(book_id, kindle_mail):
def get_attachment(file_path): def get_attachment(file_path):
'''Get file as MIMEBase message''' """Get file as MIMEBase message"""
try: try:
file_ = open(file_path, 'rb') file_ = open(file_path, 'rb')
...@@ -163,6 +243,7 @@ def get_attachment(file_path): ...@@ -163,6 +243,7 @@ def get_attachment(file_path):
'permissions?')) 'permissions?'))
return None return None
def get_valid_filename(value, replace_whitespace=True): def get_valid_filename(value, replace_whitespace=True):
""" """
Returns the given string converted to a string that can be used for a clean Returns the given string converted to a string that can be used for a clean
...@@ -178,6 +259,7 @@ def get_valid_filename(value, replace_whitespace=True): ...@@ -178,6 +259,7 @@ def get_valid_filename(value, replace_whitespace=True):
value = value.replace(u"\u00DF", "ss") value = value.replace(u"\u00DF", "ss")
return value return value
def get_normalized_author(value): def get_normalized_author(value):
""" """
Normalizes sorted author name Normalizes sorted author name
...@@ -187,13 +269,14 @@ def get_normalized_author(value): ...@@ -187,13 +269,14 @@ def get_normalized_author(value):
value = " ".join(value.split(", ")[::-1]) value = " ".join(value.split(", ")[::-1])
return value return value
def update_dir_stucture(book_id): def update_dir_stucture(book_id):
db.session.connection().connection.connection.create_function("title_sort",1,db.title_sort) db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
path = os.path.join(config.DB_ROOT, book.path) path = os.path.join(config.DB_ROOT, book.path)
authordir = book.path.split(os.sep)[0] authordir = book.path.split(os.sep)[0]
new_authordir=get_valid_filename(book.authors[0].name, False) new_authordir = get_valid_filename(book.authors[0].name, False)
titledir = book.path.split(os.sep)[1] titledir = book.path.split(os.sep)[1]
new_titledir = get_valid_filename(book.title, False) + " (" + str(book_id) + ")" new_titledir = get_valid_filename(book.title, False) + " (" + str(book_id) + ")"
...@@ -208,4 +291,3 @@ def update_dir_stucture(book_id): ...@@ -208,4 +291,3 @@ def update_dir_stucture(book_id):
os.renames(path, new_author_path) os.renames(path, new_author_path)
book.path = new_authordir + os.sep + book.path.split(os.sep)[1] book.path = new_authordir + os.sep + book.path.split(os.sep)[1]
db.session.commit() db.session.commit()
...@@ -27,7 +27,8 @@ ...@@ -27,7 +27,8 @@
<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>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button> <button type="submit" name="submit" value="submit" class="btn btn-default">{{_('Save settings')}}</button>
<button type="submit" name="test" value="test" class="btn btn-default">{{_('Save settings and send Test E-Mail')}}</button>
<a href="{{ url_for('user_list') }}" class="btn btn-default">{{_('Back')}}</a> <a href="{{ url_for('user_list') }}" class="btn btn-default">{{_('Back')}}</a>
</form> </form>
......
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
<div class="discover"> <h3>{{_('Linked libraries')}}</h3>
<h2>{{bookcounter}} {{_('Books in this Library')}}</h2>
<h2>{{authorcounter}} {{_('Authors in this Library')}}</h2> <table class="table">
</div> <thead>
<tr>
<th>{{_('Program library')}}</th>
<th>{{_('Installed Version')}}</th>
</tr>
</thead>
<tbody>
<tr>
<th>Python</th>
<td>{{Versions['PythonVersion']}}</td>
</tr>
<tr>
<th>Kindlegen</th>
<td>{{Versions['KindlegenVersion']}}</td>
</tr>
<tr>
<th>ImageMagick</th>
<td>{{Versions['ImageVersion']}}</td>
</tr>
<tr>
<th>PyPDF2</th>
<td>{{Versions['PyPdfVersion']}}</td>
</tr>
</tbody>
</table>
<h3>{{_('Calibre library statistics')}}</h3>
<table class="table">
<tbody>
<tr>
<th>{{bookcounter}}</th>
<td>{{_('Books in this Library')}}</td>
</tr>
<tr>
<th>{{authorcounter}}</th>
<td>{{_('Authors in this Library')}}</td>
</tr>
</tbody>
</table>
{% endblock %} {% endblock %}
...@@ -40,7 +40,6 @@ ...@@ -40,7 +40,6 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}> <input type="checkbox" name="show_random" {% if content.random_books %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label> <label for="show_random">{{_('Show random books')}}</label>
...@@ -62,6 +61,8 @@ ...@@ -62,6 +61,8 @@
<label for="show_category">{{_('Show category selection')}}</label> <label for="show_category">{{_('Show category selection')}}</label>
</div> </div>
{% if g.user and g.user.role_admin() and not profile %}
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}> <input type="checkbox" name="admin_role" id="admin_role" {% if content.role_admin() %}checked{% endif %}>
<label for="admin_role">{{_('Admin user')}}</label> <label for="admin_role">{{_('Admin user')}}</label>
......
This diff is collapsed.
This diff is collapsed.
...@@ -21,18 +21,19 @@ ROLE_EDIT = 8 ...@@ -21,18 +21,19 @@ ROLE_EDIT = 8
ROLE_PASSWD = 16 ROLE_PASSWD = 16
DEFAULT_PASS = "admin123" DEFAULT_PASS = "admin123"
class User(Base): class User(Base):
__tablename__ = 'user' __tablename__ = 'user'
id = Column(Integer, primary_key = True) id = Column(Integer, primary_key=True)
nickname = Column(String(64), unique = True) nickname = Column(String(64), unique=True)
email = Column(String(120), unique = True, default = "") email = Column(String(120), unique=True, default="")
role = Column(SmallInteger, default = ROLE_USER) role = Column(SmallInteger, default=ROLE_USER)
password = Column(String) password = Column(String)
kindle_mail = Column(String(120), default="") kindle_mail = Column(String(120), default="")
shelf = relationship('Shelf', backref = 'user', lazy = 'dynamic') shelf = relationship('Shelf', backref='user', lazy='dynamic')
whislist = relationship('Whislist', backref = 'user', lazy = 'dynamic') whislist = relationship('Whislist', backref='user', lazy='dynamic')
downloads = relationship('Downloads', backref= 'user', lazy = 'dynamic') downloads = relationship('Downloads', backref='user', lazy='dynamic')
locale = Column(String(2), default="en") locale = Column(String(2), default="en")
random_books = Column(Integer, default=1) random_books = Column(Integer, default=1)
language_books = Column(Integer, default=1) language_books = Column(Integer, default=1)
...@@ -43,26 +44,31 @@ class User(Base): ...@@ -43,26 +44,31 @@ class User(Base):
def is_authenticated(self): def is_authenticated(self):
return True return True
def role_admin(self): def role_admin(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_ADMIN == ROLE_ADMIN else False return True if self.role & ROLE_ADMIN == ROLE_ADMIN else False
else: else:
return False return False
def role_download(self): def role_download(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_DOWNLOAD == ROLE_DOWNLOAD else False return True if self.role & ROLE_DOWNLOAD == ROLE_DOWNLOAD else False
else: else:
return False return False
def role_upload(self): def role_upload(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_UPLOAD == ROLE_UPLOAD else False return True if self.role & ROLE_UPLOAD == ROLE_UPLOAD else False
else: else:
return False return False
def role_edit(self): def role_edit(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_EDIT == ROLE_EDIT else False return True if self.role & ROLE_EDIT == ROLE_EDIT else False
else: else:
return False return False
def role_passwd(self): def role_passwd(self):
if self.role is not None: if self.role is not None:
return True if self.role & ROLE_PASSWD == ROLE_PASSWD else False return True if self.role & ROLE_PASSWD == ROLE_PASSWD else False
...@@ -96,20 +102,20 @@ class User(Base): ...@@ -96,20 +102,20 @@ class User(Base):
def show_category(self): def show_category(self):
return self.category_books return self.category_books
def __repr__(self): def __repr__(self):
return '<User %r>' % (self.nickname) return '<User %r>' % self.nickname
class Shelf(Base): class Shelf(Base):
__tablename__ = 'shelf' __tablename__ = 'shelf'
id = Column(Integer, primary_key = True) id = Column(Integer, primary_key=True)
name = Column(String) name = Column(String)
is_public = Column(Integer, default=0) is_public = Column(Integer, default=0)
user_id = Column(Integer, ForeignKey('user.id')) user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self): def __repr__(self):
return '<Shelf %r>' % (self.name) return '<Shelf %r>' % self.name
class Whislist(Base): class Whislist(Base):
...@@ -124,7 +130,7 @@ class Whislist(Base): ...@@ -124,7 +130,7 @@ class Whislist(Base):
pass pass
def __repr__(self): def __repr__(self):
return '<Whislist %r>' % (self.name) return '<Whislist %r>' % self.name
class BookShelf(Base): class BookShelf(Base):
...@@ -135,7 +141,7 @@ class BookShelf(Base): ...@@ -135,7 +141,7 @@ class BookShelf(Base):
shelf = Column(Integer, ForeignKey('shelf.id')) shelf = Column(Integer, ForeignKey('shelf.id'))
def __repr__(self): def __repr__(self):
return '<Book %r>' % (self.id) return '<Book %r>' % self.id
class Downloads(Base): class Downloads(Base):
...@@ -146,7 +152,8 @@ class Downloads(Base): ...@@ -146,7 +152,8 @@ class Downloads(Base):
user_id = Column(Integer, ForeignKey('user.id')) user_id = Column(Integer, ForeignKey('user.id'))
def __repr__(self): def __repr__(self):
return '<Download %r' % (self.book_id) return '<Download %r' % self.book_id
class Whish(Base): class Whish(Base):
__tablename__ = 'whish' __tablename__ = 'whish'
...@@ -157,7 +164,8 @@ class Whish(Base): ...@@ -157,7 +164,8 @@ class Whish(Base):
wishlist = Column(Integer, ForeignKey('wishlist.id')) wishlist = Column(Integer, ForeignKey('wishlist.id'))
def __repr__(self): def __repr__(self):
return '<Whish %r>' % (self.title) return '<Whish %r>' % self.title
class Settings(Base): class Settings(Base):
__tablename__ = 'settings' __tablename__ = 'settings'
...@@ -174,12 +182,13 @@ class Settings(Base): ...@@ -174,12 +182,13 @@ class Settings(Base):
#return '<Smtp %r>' % (self.mail_server) #return '<Smtp %r>' % (self.mail_server)
pass pass
def migrate_Database(): def migrate_Database():
try: try:
session.query(exists().where(User.random_books)).scalar() session.query(exists().where(User.random_books)).scalar()
session.commit() session.commit()
except exc.OperationalError: # Database is not compatible, some rows are missing except exc.OperationalError: # Database is not compatible, some rows are missing
conn=engine.connect() conn = engine.connect()
conn.execute("ALTER TABLE user ADD column random_books INTEGER DEFAULT 1") conn.execute("ALTER TABLE user ADD column random_books INTEGER DEFAULT 1")
conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'") conn.execute("ALTER TABLE user ADD column locale String(2) DEFAULT 'en'")
conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'") conn.execute("ALTER TABLE user ADD column default_language String(3) DEFAULT 'all'")
...@@ -208,6 +217,7 @@ def create_default_config(): ...@@ -208,6 +217,7 @@ def create_default_config():
session.add(settings) session.add(settings)
session.commit() session.commit()
def get_mail_settings(): def get_mail_settings():
settings = session.query(Settings).first() settings = session.query(Settings).first()
...@@ -225,6 +235,7 @@ def get_mail_settings(): ...@@ -225,6 +235,7 @@ def get_mail_settings():
return data return data
def create_admin_user(): def create_admin_user():
user = User() user = User()
user.nickname = "admin" user.nickname = "admin"
...@@ -251,4 +262,3 @@ if not os.path.exists(dbpath): ...@@ -251,4 +262,3 @@ if not os.path.exists(dbpath):
pass pass
else: else:
migrate_Database() migrate_Database()
...@@ -9,6 +9,8 @@ BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, d ...@@ -9,6 +9,8 @@ BookMeta = namedtuple('BookMeta', 'file_path, extension, title, author, cover, d
""" """
:rtype: BookMeta :rtype: BookMeta
""" """
def upload(file): def upload(file):
tmp_dir = os.path.join(gettempdir(), 'calibre_web') tmp_dir = os.path.join(gettempdir(), 'calibre_web')
...@@ -23,7 +25,3 @@ def upload(file): ...@@ -23,7 +25,3 @@ def upload(file):
file.save(tmp_file_path) file.save(tmp_file_path)
meta = book_formats.process(tmp_file_path, filename_root, file_extension) meta = book_formats.process(tmp_file_path, filename_root, file_extension)
return meta return meta
This diff is collapsed.
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