Merge remote-tracking branch 'refs/remotes/janeczku/master'

pull/140/head
Radosław Kierznowski 8 years ago
commit 76c907484b

@ -56,6 +56,10 @@ books_languages_link = Table('books_languages_link', Base.metadata,
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True) Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
) )
books_publishers_link = Table('books_publishers_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('publisher', Integer, ForeignKey('publishers.id'), primary_key=True)
)
class Identifiers(Base): class Identifiers(Base):
__tablename__ = 'identifiers' __tablename__ = 'identifiers'
@ -182,6 +186,21 @@ class Languages(Base):
def __repr__(self): def __repr__(self):
return u"<Languages('{0}')>".format(self.lang_code) return u"<Languages('{0}')>".format(self.lang_code)
class Publishers(Base):
__tablename__ = 'publishers'
id = Column(Integer, primary_key=True)
name = Column(String)
sort = Column(String)
def __init__(self, name,sort):
self.name = name
self.sort = sort
def __repr__(self):
return u"<Publishers('{0},{1}')>".format(self.name, self.sort)
class Data(Base): class Data(Base):
__tablename__ = 'data' __tablename__ = 'data'
@ -224,6 +243,7 @@ class Books(Base):
series = relationship('Series', secondary=books_series_link, backref='books') series = relationship('Series', secondary=books_series_link, backref='books')
ratings = relationship('Ratings', secondary=books_ratings_link, backref='books') ratings = relationship('Ratings', secondary=books_ratings_link, backref='books')
languages = relationship('Languages', secondary=books_languages_link, backref='books') languages = relationship('Languages', secondary=books_languages_link, backref='books')
publishers = relationship('Publishers', secondary=books_publishers_link, backref='books')
identifiers = relationship('Identifiers', backref='books') identifiers = relationship('Identifiers', backref='books')
def __init__(self, title, sort, author_sort, timestamp, pubdate, series_index, last_modified, path, has_cover, def __init__(self, title, sort, author_sort, timestamp, pubdate, series_index, last_modified, path, has_cover,
@ -274,7 +294,7 @@ def setup_db():
return False return False
dbpath = os.path.join(config.config_calibre_dir, "metadata.db") dbpath = os.path.join(config.config_calibre_dir, "metadata.db")
engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False) engine = create_engine('sqlite:///{0}'.format(dbpath.encode('utf-8')), echo=False, isolation_level="SERIALIZABLE")
try: try:
conn = engine.connect() conn = engine.connect()

@ -7,13 +7,14 @@ import os
import uploader import uploader
def extractCover(zip, coverFile, tmp_file_name): def extractCover(zip, coverFile, coverpath, tmp_file_name):
if coverFile is None: if coverFile is None:
return None return None
else: else:
cf = zip.read("OPS/" + coverFile) zipCoverPath = os.path.join(coverpath , coverFile).replace('\\','/')
cf = zip.read(zipCoverPath)
prefix = os.path.splitext(tmp_file_name)[0] prefix = os.path.splitext(tmp_file_name)[0]
tmp_cover_name = prefix + "." + coverFile tmp_cover_name = prefix + '.' + os.path.basename(zipCoverPath)
image = open(tmp_cover_name, 'wb') image = open(tmp_cover_name, 'wb')
image.write(cf) image.write(cf)
image.close() image.close()
@ -32,10 +33,11 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
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)
coverpath=os.path.dirname(cfname)
p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0] p = tree.xpath('/pkg:package/pkg:metadata', namespaces=ns)[0]
epub_metadata = {} epub_metadata = {}
@ -46,11 +48,16 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
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-image']/@href", namespaces=ns)
if len(coversection) > 0: if len(coversection) > 0:
coverfile = extractCover(zip, coversection[0], tmp_file_path) coverfile = extractCover(zip, coversection[0], coverpath, tmp_file_path)
else: else:
coverfile = None coversection = tree.xpath("/pkg:package/pkg:manifest/pkg:item[@id='cover']/@href", namespaces=ns)
if len(coversection) > 0:
coverfile = extractCover(zip, coversection[0], coverpath, tmp_file_path)
else:
coverfile = None
if epub_metadata['title'] is None: if epub_metadata['title'] is None:
title = original_file_name title = original_file_name
else: else:

@ -19,6 +19,8 @@ from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText from email.MIMEText import MIMEText
from email.generator import Generator from email.generator import Generator
from email.utils import formatdate
from email.utils import make_msgid
from flask_babel import gettext as _ from flask_babel import gettext as _
import subprocess import subprocess
import threading import threading
@ -165,6 +167,8 @@ def send_mail(book_id, kindle_mail, calibrepath):
# create MIME message # create MIME message
msg = MIMEMultipart() msg = MIMEMultipart()
msg['Subject'] = _(u'Send to Kindle') msg['Subject'] = _(u'Send to Kindle')
msg['Message-Id'] = make_msgid('calibre-web')
msg['Date'] = formatdate(localtime=True)
text = _(u'This email has been sent via calibre web.') text = _(u'This email has been sent via calibre web.')
msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8')) msg.attach(MIMEText(text.encode('UTF-8'), 'plain', 'UTF-8'))

@ -36,7 +36,7 @@ $(function() {
success: function(data) { success: function(data) {
$('#spinner').show(); $('#spinner').show();
displaytext=data.text; displaytext=data.text;
window.setTimeout(restartTimer, 3000);} setTimeout(restartTimer, 3000);}
}); });
}); });
$("#shutdown").click(function() { $("#shutdown").click(function() {
@ -50,7 +50,7 @@ $(function() {
}); });
$("#check_for_update").click(function() { $("#check_for_update").click(function() {
var button_text = $("#check_for_update").html(); var button_text = $("#check_for_update").html();
$("#check_for_update").html('Checking...'); $("#check_for_update").html('...');
$.ajax({ $.ajax({
dataType: 'json', dataType: 'json',
url: window.location.pathname+"/../../get_update_status", url: window.location.pathname+"/../../get_update_status",
@ -110,7 +110,8 @@ function updateTimer() {
$('#UpdateprogressDialog #updateFinished').removeClass('hidden'); $('#UpdateprogressDialog #updateFinished').removeClass('hidden');
$("#check_for_update").removeClass('hidden'); $("#check_for_update").removeClass('hidden');
$("#perform_update").addClass('hidden'); $("#perform_update").addClass('hidden');
} },
timeout:2000
}); });
} }

@ -50,6 +50,7 @@
{% if entry.identifiers|length > 0 %} {% if entry.identifiers|length > 0 %}
<div class="identifiers"> <div class="identifiers">
<p>
<span class="glyphicon glyphicon-link"></span> <span class="glyphicon glyphicon-link"></span>
{% for identifier in entry.identifiers %} {% for identifier in entry.identifiers %}
<a href="{{identifier}}" target="_blank" class="btn btn-xs btn-success" role="button">{{identifier.formatType()}}</a> <a href="{{identifier}}" target="_blank" class="btn btn-xs btn-success" role="button">{{identifier.formatType()}}</a>
@ -66,10 +67,16 @@
{% for tag in entry.tags %} {% for tag in entry.tags %}
<a href="{{ url_for('category', id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a> <a href="{{ url_for('category', id=tag.id) }}" class="btn btn-xs btn-info" role="button">{{tag.name}}</a>
{%endfor%} {%endfor%}
</div> </div>
</p> </p>
{% endif %} {% endif %}
{% if entry.publishers|length > 0 %}
<div class="publishers">
<p>
<span>{{_('Publisher')}}:{% for publisher in entry.publishers %} {{publisher.name}}{% if not loop.last %},{% endif %}{% endfor %}</span>
</p>
</div>
{% endif %}
{% if entry.pubdate[:10] != '0101-01-01' %} {% if entry.pubdate[:10] != '0101-01-01' %}
<p>{{_('Publishing date')}}: {{entry.pubdate|formatdate}} </p> <p>{{_('Publishing date')}}: {{entry.pubdate|formatdate}} </p>
{% endif %} {% endif %}
@ -125,10 +132,10 @@
</div> </div>
{% endif %} {% endif %}
{% if g.user.is_authenticated %} {% if g.user.kindle_mail and g.user.is_authenticated %}
{% if g.user.kindle_mail %}
<a href="{{url_for('send_to_kindle', book_id=entry.id)}}" id="sendbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-send"></span> {{_('Send to Kindle')}}</a> <a href="{{url_for('send_to_kindle', book_id=entry.id)}}" id="sendbtn" class="btn btn-primary" role="button"><span class="glyphicon glyphicon-send"></span> {{_('Send to Kindle')}}</a>
{% endif %} {% endif %}
{% if (g.user.role_download() and g.user.is_anonymous()) or g.user.is_authenticated %}
<div class="btn-group" role="group"> <div class="btn-group" role="group">
<button id="btnGroupDrop2" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button id="btnGroupDrop2" type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-eye-open"></span> {{_('Read in browser')}} <span class="glyphicon glyphicon-eye-open"></span> {{_('Read in browser')}}

@ -2,11 +2,17 @@
{% block body %} {% block body %}
<h1>{{title}}</h1> <h1>{{title}}</h1>
<div class="container"> <div class="container">
<div class="col-sm-6">
{% for lang in languages %} {% for lang in languages %}
{% if loop.index0 == (loop.length/2)|int and loop.length > 20 %}
</div>
<div class="col-sm-6">
{% endif %}
<div class="row"> <div class="row">
<div class="col-xs-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div> <div class="col-xs-1" align="left"><span class="badge">{{lang_counter[loop.index0].bookcount}}</span></div>
<div class="col-xs-6"><a id="list_{{loop.index0}}" href="{{url_for('language', name=lang.lang_code)}}">{{lang.name}}</a></div> <div class="col-xs-6"><a id="list_{{loop.index0}}" href="{{url_for('language', name=lang.lang_code)}}">{{lang.name}}</a></div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
{% endblock %} {% endblock %}

@ -2,11 +2,17 @@
{% block body %} {% block body %}
<h1>{{title}}</h1> <h1>{{title}}</h1>
<div class="container"> <div class="container">
<div class="col-sm-6">
{% for entry in entries %} {% for entry in entries %}
{% if loop.index0 == (loop.length/2)|int and loop.length > 20 %}
</div>
<div class="col-sm-6">
{% endif %}
<div class="row"> <div class="row">
<div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div> <div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div>
<div class="col-xs-6"><a id="list_{{loop.index0}}" href="{{url_for(folder, id=entry[0].id )}}">{{entry[0].name}}</a></div> <div class="col-xs-6"><a id="list_{{loop.index0}}" href="{{url_for(folder, id=entry[0].id )}}">{{entry[0].name}}</a></div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
{% endblock %} {% endblock %}

@ -10,63 +10,67 @@
<label for="bookAuthor">{{_('Author')}}</label> <label for="bookAuthor">{{_('Author')}}</label>
<input type="text" class="form-control typeahead" name="author_name" id="bookAuthor" value="" autocomplete="off"> <input type="text" class="form-control typeahead" name="author_name" id="bookAuthor" value="" autocomplete="off">
</div> </div>
<label for="Tags">{{_('Tags')}}</label>
<div class="form-group"> <div class="form-group">
<label for="Publisher">{{_('Publisher')}}</label>
<input type="text" class="form-control" name="publisher" id="publisher" value="">
</div>
<label for="include_tag">{{_('Tags')}}</label>
<div class="form-group" id="test">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for tag in tags %} {% for tag in tags %}
<label id="tag_{{tag.id}}" class="btn btn-primary tags_click"> <label id="tag_{{tag.id}}" class="btn btn-primary tags_click">
<input type="checkbox" autocomplete="off" name="include_tag" value="{{tag.id}}">{{tag.name}}</input> <input type="checkbox" autocomplete="off" name="include_tag" id="include_tag" value="{{tag.id}}">{{tag.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<label for="Tags">{{_('Exclude Tags')}}</label> <label for="exclude_tag">{{_('Exclude Tags')}}</label>
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for tag in tags %} {% for tag in tags %}
<label id="tag_{{tag.id}}" class="btn btn-danger tags_click"> <label id="tag_{{tag.id}}" class="btn btn-danger tags_click">
<input type="checkbox" autocomplete="off" name="exclude_tag" value="{{tag.id}}">{{tag.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_tag" id="exclude_tag" value="{{tag.id}}">{{tag.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<label for="Series">{{_('Series')}}</label> <label for="include_serie">{{_('Series')}}</label>
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for serie in series %} {% for serie in series %}
<label id="serie_{{serie.id}}" class="btn btn-primary serie_click"> <label id="serie_{{serie.id}}" class="btn btn-primary serie_click">
<input type="checkbox" autocomplete="off" name="include_serie" value="{{serie.id}}">{{serie.name}}</input> <input type="checkbox" autocomplete="off" name="include_serie" id="include_serie" value="{{serie.id}}">{{serie.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<label for="Series">{{_('Exclude Series')}}</label> <label for="exclude_serie">{{_('Exclude Series')}}</label>
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for serie in series %} {% for serie in series %}
<label id="serie_{{serie.id}}" class="btn btn-danger serie_click"> <label id="serie_{{serie.id}}" class="btn btn-danger serie_click">
<input type="checkbox" autocomplete="off" name="exclude_serie" value="{{serie.id}}">{{serie.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_serie" id="exclude_serie" value="{{serie.id}}">{{serie.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{% if languages %} {% if languages %}
<label for="Languages">{{_('Languages')}}</label> <label for="include_language">{{_('Languages')}}</label>
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for language in languages %} {% for language in languages %}
<label id="language_{{language.id}}" class="btn btn-primary serie_click"> <label id="language_{{language.id}}" class="btn btn-primary serie_click">
<input type="checkbox" autocomplete="off" name="include_language" value="{{language.id}}">{{language.name}}</input> <input type="checkbox" autocomplete="off" name="include_language" id="include_language" value="{{language.id}}">{{language.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<label for="Languages">{{_('Exclude Languages')}}</label> <label for="exclude_language">{{_('Exclude Languages')}}</label>
<div class="form-group"> <div class="form-group">
<div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons"> <div class="btn-toolbar btn-toolbar-lg" data-toggle="buttons">
{% for language in languages %} {% for language in languages %}
<label id="language_{{language.id}}" class="btn btn-danger language_click"> <label id="language_{{language.id}}" class="btn btn-danger language_click">
<input type="checkbox" autocomplete="off" name="exclude_language" value="{{language.id}}">{{language.name}}</input> <input type="checkbox" autocomplete="off" name="exclude_language" id="exclude_language" value="{{language.id}}">{{language.name}}</input>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>

@ -1,7 +1,27 @@
{% extends "layout.html" %} {% extends "layout.html" %}
{% block body %} {% block body %}
<h3>{{_('Calibre library statistics')}}</h3>
<table id="stats" 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>
<tr>
<th>{{categorycounter}}</th>
<td>{{_('Categories in this Library')}}</td>
</tr>
<tr>
<th>{{seriecounter}}</th>
<td>{{_('Series in this Library')}}</td>
</tr>
</tbody>
</table>
<h3>{{_('Linked libraries')}}</h3> <h3>{{_('Linked libraries')}}</h3>
<table id="libs" class="table"> <table id="libs" class="table">
<thead> <thead>
<tr> <tr>
@ -24,30 +44,51 @@
</tr> </tr>
<tr> <tr>
<th>PyPDF2</th> <th>PyPDF2</th>
<td>{{versions['PyPdfVersion']}}</td> <td>v{{versions['PyPdfVersion']}}</td>
</tr> </tr>
</tbody>
</table>
<h3>{{_('Calibre library statistics')}}</h3>
<table id="stats" class="table">
<tbody>
<tr> <tr>
<th>{{bookcounter}}</th> <th>Babel</th>
<td>{{_('Books in this Library')}}</td> <td>v{{versions['babel']}}</td>
</tr> </tr>
<tr> <tr>
<th>{{authorcounter}}</th> <th>SqlAlchemy</th>
<td>{{_('Authors in this Library')}}</td> <td>v{{versions['sqlalchemy']}}</td>
</tr> </tr>
<tr> <tr>
<th>{{categorycounter}}</th> <th>Flask</th>
<td>{{_('Categories in this Library')}}</td> <td>v{{versions['flask']}}</td>
</tr> </tr>
<tr> <tr>
<th>{{seriecounter}}</th> <th>Flask Login</th>
<td>{{_('Series in this Library')}}</td> <td>v{{versions['flasklogin']}}</td>
</tr>
<tr>
<th>Flask Principal</th>
<td>v{{versions['flask_principal']}}</td>
</tr>
<tr>
<th>Tornado web server</th>
<td>v{{versions['tornado']}}</td>
</tr>
<tr>
<th>ISO639 Languages</th>
<td>v{{versions['iso639']}}</td>
</tr>
<tr>
<th>Requests</th>
<td>v{{versions['requests']}}</td>
</tr>
<tr>
<th>SQlite</th>
<td>v{{versions['sqlite']}}</td>
</tr>
<tr>
<th>Pysqlite</th>
<td>v{{versions['pysqlite']}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
{% endblock %} {% endblock %}

@ -40,6 +40,8 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
<div class="col-sm-6">
<div class="form-group"> <div class="form-group">
<input type="checkbox" name="show_random" id="show_random" {% if content.show_random_books() %}checked{% endif %}> <input type="checkbox" name="show_random" id="show_random" {% if content.show_random_books() %}checked{% endif %}>
<label for="show_random">{{_('Show random books')}}</label> <label for="show_random">{{_('Show random books')}}</label>
@ -72,7 +74,8 @@
<input type="checkbox" name="show_detail_random" id="show_detail_random" {% if content.show_detail_random() %}checked{% endif %}> <input type="checkbox" name="show_detail_random" id="show_detail_random" {% if content.show_detail_random() %}checked{% endif %}>
<label for="show_detail_random">{{_('Show random books in detail view')}}</label> <label for="show_detail_random">{{_('Show random books in detail view')}}</label>
</div> </div>
</div>
<div class="col-sm-6">
{% if g.user and g.user.role_admin() and not profile %} {% if g.user and g.user.role_admin() and not profile %}
{% if not content.role_anonymous() %} {% if not content.role_anonymous() %}
<div class="form-group"> <div class="form-group">
@ -106,13 +109,17 @@
</label> </label>
</div> </div>
{% endif %} {% endif %}
</div>
<div class="col-sm-12">
<button type="submit" id="submit" class="btn btn-default">{{_('Submit')}}</button> <button type="submit" id="submit" class="btn btn-default">{{_('Submit')}}</button>
{% if not profile %} {% if not profile %}
<a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a> <a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a>
</div>
{% endif %} {% endif %}
</form> </form>
{% if downloads %} {% if downloads %}
<div class="col-sm-12">
<h2>{{_('Recent Downloads')}}</h2> <h2>{{_('Recent Downloads')}}</h2>
{% for entry in downloads %} {% for entry in downloads %}
<div class="col-sm-2"> <div class="col-sm-2">
@ -121,6 +128,7 @@
</a> </a>
</div> </div>
{% endfor %} {% endfor %}
</div>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

@ -61,31 +61,31 @@ msgstr "游客"
#: cps/web.py:734 #: cps/web.py:734
msgid "Requesting update package" msgid "Requesting update package"
msgstr "" msgstr "正在请求更新包"
#: cps/web.py:735 #: cps/web.py:735
msgid "Downloading update package" msgid "Downloading update package"
msgstr "" msgstr "正在下载更新包"
#: cps/web.py:736 #: cps/web.py:736
msgid "Unzipping update package" msgid "Unzipping update package"
msgstr "" msgstr "正在解压更新包"
#: cps/web.py:737 #: cps/web.py:737
msgid "Files are replaced" msgid "Files are replaced"
msgstr "" msgstr "文件已替换"
#: cps/web.py:738 #: cps/web.py:738
msgid "Database connections are closed" msgid "Database connections are closed"
msgstr "" msgstr "数据库连接已关闭"
#: cps/web.py:739 #: cps/web.py:739
msgid "Server is stopped" msgid "Server is stopped"
msgstr "" msgstr "服务器已停止"
#: cps/web.py:740 #: cps/web.py:740
msgid "Update finished, please press okay and reload page" msgid "Update finished, please press okay and reload page"
msgstr "" msgstr "更新完成,请按确定并刷新页面"
#: cps/web.py:810 #: cps/web.py:810
msgid "Latest Books" msgid "Latest Books"
@ -97,7 +97,7 @@ msgstr "热门书籍(最多下载)"
#: cps/web.py:845 #: cps/web.py:845
msgid "Best rated books" msgid "Best rated books"
msgstr "" msgstr "最高评分书籍"
#: cps/templates/index.xml:36 cps/web.py:854 #: cps/templates/index.xml:36 cps/web.py:854
msgid "Random Books" msgid "Random Books"
@ -108,9 +108,9 @@ msgid "Author list"
msgstr "作者列表" msgstr "作者列表"
#: cps/web.py:878 #: cps/web.py:878
#, python-format #, python-forma
msgid "Author: %(name)s" msgid "Author: %(name)s"
msgstr "" msgstr "作者: %(name)s"
#: cps/web.py:880 cps/web.py:908 cps/web.py:1007 cps/web.py:1235 #: cps/web.py:880 cps/web.py:908 cps/web.py:1007 cps/web.py:1235
#: cps/web.py:2115 #: cps/web.py:2115
@ -150,7 +150,7 @@ msgstr "统计"
#: cps/web.py:1061 #: cps/web.py:1061
msgid "Server restarted, please reload page" msgid "Server restarted, please reload page"
msgstr "" msgstr "服务器已重启,请刷新页面"
#: cps/web.py:1063 #: cps/web.py:1063
msgid "Performing shutdown of server, please close window" msgid "Performing shutdown of server, please close window"
@ -158,7 +158,7 @@ msgstr "正在关闭服务器,请关闭窗口"
#: cps/web.py:1073 #: cps/web.py:1073
msgid "Update done" msgid "Update done"
msgstr "" msgstr "更新完成"
#: cps/web.py:1147 cps/web.py:1160 #: cps/web.py:1147 cps/web.py:1160
msgid "search" msgid "search"
@ -475,11 +475,11 @@ msgstr "管理"
#: cps/templates/admin.html:80 #: cps/templates/admin.html:80
msgid "Current commit timestamp" msgid "Current commit timestamp"
msgstr "" msgstr "当前提交时间戳"
#: cps/templates/admin.html:81 #: cps/templates/admin.html:81
msgid "Newest commit timestamp" msgid "Newest commit timestamp"
msgstr "" msgstr "最新提交时间戳"
#: cps/templates/admin.html:83 #: cps/templates/admin.html:83
msgid "Restart Calibre-web" msgid "Restart Calibre-web"
@ -491,11 +491,11 @@ msgstr "停止 Calibre-web"
#: cps/templates/admin.html:85 #: cps/templates/admin.html:85
msgid "Check for update" msgid "Check for update"
msgstr "" msgstr "检查更新"
#: cps/templates/admin.html:86 #: cps/templates/admin.html:86
msgid "Perform Update" msgid "Perform Update"
msgstr "" msgstr "执行更新"
#: cps/templates/admin.html:96 #: cps/templates/admin.html:96
msgid "Do you really want to restart Calibre-web?" msgid "Do you really want to restart Calibre-web?"
@ -519,7 +519,7 @@ msgstr "您确定要关闭 Calibre-web 吗?"
#: cps/templates/admin.html:127 #: cps/templates/admin.html:127
msgid "Updating, please do not reload page" msgid "Updating, please do not reload page"
msgstr "" msgstr "正在更新,请不要刷新页面"
#: cps/templates/book_edit.html:16 cps/templates/search_form.html:6 #: cps/templates/book_edit.html:16 cps/templates/search_form.html:6
msgid "Book Title" msgid "Book Title"
@ -610,7 +610,7 @@ msgstr "启用注册"
#: cps/templates/config_edit.html:52 #: cps/templates/config_edit.html:52
msgid "Default Settings for new users" msgid "Default Settings for new users"
msgstr "" msgstr "新用户默认设置"
#: cps/templates/config_edit.html:55 cps/templates/user_edit.html:80 #: cps/templates/config_edit.html:55 cps/templates/user_edit.html:80
msgid "Admin user" msgid "Admin user"
@ -651,7 +651,7 @@ msgstr "语言"
#: cps/templates/detail.html:74 #: cps/templates/detail.html:74
msgid "Publishing date" msgid "Publishing date"
msgstr "" msgstr "出版日期"
#: cps/templates/detail.html:106 #: cps/templates/detail.html:106
msgid "Description:" msgid "Description:"
@ -723,11 +723,11 @@ msgstr "热门书籍"
#: cps/templates/index.xml:19 #: cps/templates/index.xml:19
msgid "Popular publications from this catalog based on Downloads." msgid "Popular publications from this catalog based on Downloads."
msgstr "" msgstr "基于下载数的热门书籍"
#: cps/templates/index.xml:22 cps/templates/layout.html:129 #: cps/templates/index.xml:22 cps/templates/layout.html:129
msgid "Best rated Books" msgid "Best rated Books"
msgstr "" msgstr "最高评分书籍"
#: cps/templates/index.xml:26 #: cps/templates/index.xml:26
msgid "Popular publications from this catalog based on Rating." msgid "Popular publications from this catalog based on Rating."
@ -933,11 +933,11 @@ msgstr "个作者在此书库"
#: cps/templates/stats.html:45 #: cps/templates/stats.html:45
msgid "Categories in this Library" msgid "Categories in this Library"
msgstr "" msgstr "个分类在此书库"
#: cps/templates/stats.html:49 #: cps/templates/stats.html:49
msgid "Series in this Library" msgid "Series in this Library"
msgstr "" msgstr "个丛书在此书库"
#: cps/templates/user_edit.html:23 #: cps/templates/user_edit.html:23
msgid "Kindle E-Mail" msgid "Kindle E-Mail"
@ -961,7 +961,7 @@ msgstr "显示热门书籍"
#: cps/templates/user_edit.html:53 #: cps/templates/user_edit.html:53
msgid "Show best rated books" msgid "Show best rated books"
msgstr "" msgstr "显示最高评分书籍"
#: cps/templates/user_edit.html:57 #: cps/templates/user_edit.html:57
msgid "Show language selection" msgid "Show language selection"

@ -7,7 +7,6 @@ from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import * from sqlalchemy.orm import *
from flask_login import AnonymousUserMixin from flask_login import AnonymousUserMixin
import os import os
import traceback
import logging import logging
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from flask_babel import gettext as _ from flask_babel import gettext as _

@ -4,8 +4,9 @@ import mimetypes
import logging import logging
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
import textwrap import textwrap
from flask import Flask, render_template, session, request, Response, redirect, url_for, send_from_directory, \ from flask import Flask, render_template, request, Response, redirect, url_for, send_from_directory, \
make_response, g, flash, abort make_response, g, flash, abort
from flask import __version__ as flaskVersion
import ub import ub
from ub import config from ub import config
import helper import helper
@ -14,9 +15,12 @@ import errno
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from sqlalchemy.sql.expression import false from sqlalchemy.sql.expression import false
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy import __version__ as sqlalchemyVersion
from math import ceil from math import ceil
from flask_login import LoginManager, login_user, logout_user, login_required, current_user from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from flask_login import __version__ as flask_loginVersion
from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed from flask_principal import Principal, Identity, AnonymousIdentity, identity_changed
from flask_login import __version__ as flask_principalVersion
from flask_babel import Babel from flask_babel import Babel
from flask_babel import gettext as _ from flask_babel import gettext as _
import requests import requests
@ -24,6 +28,7 @@ import zipfile
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from babel import Locale as LC from babel import Locale as LC
from babel import negotiate_locale from babel import negotiate_locale
from babel import __version__ as babelVersion
from babel.dates import format_date from babel.dates import format_date
from functools import wraps from functools import wraps
import base64 import base64
@ -32,16 +37,16 @@ import json
import urllib import urllib
import datetime import datetime
from iso639 import languages as isoLanguages from iso639 import languages as isoLanguages
from iso639 import __version__ as iso639Version
from uuid import uuid4 from uuid import uuid4
import os.path import os.path
import sys import sys
import subprocess import subprocess
import re import re
import db import db
import thread
from shutil import move, copyfile from shutil import move, copyfile
from tornado.ioloop import IOLoop from tornado.ioloop import IOLoop
from tornado import version as tornadoVersion
try: try:
from wand.image import Image from wand.image import Image
@ -51,6 +56,7 @@ except ImportError, e:
use_generic_pdf_cover = True use_generic_pdf_cover = True
from cgi import escape from cgi import escape
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz', 'cbt', 'djvu', 'prc', 'doc', 'docx', 'fb2'])
# Proxy Helper class # Proxy Helper class
class ReverseProxied(object): class ReverseProxied(object):
@ -473,8 +479,10 @@ def feed_search(term):
filter = True filter = True
if term: if term:
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")), entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")),
db.Books.authors.any(db.Authors.name.like("%" + term + "%")), db.Books.series.any(db.Series.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all() db.Books.authors.any(db.Authors.name.like("%" + term + "%")),
db.Books.publishers.any(db.Publishers.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all()
entriescount = len(entries) if len(entries) > 0 else 1 entriescount = len(entries) if len(entries) > 0 else 1
pagination = Pagination(1, entriescount, entriescount) pagination = Pagination(1, entriescount, entriescount)
xml = render_title_template('feed.xml', searchterm=term, entries=entries, pagination=pagination) xml = render_title_template('feed.xml', searchterm=term, entries=entries, pagination=pagination)
@ -1039,6 +1047,17 @@ def stats():
if re.search('Amazon kindlegen\(', lines): if re.search('Amazon kindlegen\(', lines):
versions['KindlegenVersion'] = lines versions['KindlegenVersion'] = lines
versions['PythonVersion'] = sys.version versions['PythonVersion'] = sys.version
versions['babel'] = babelVersion
versions['sqlalchemy'] = sqlalchemyVersion
versions['flask'] = flaskVersion
versions['flasklogin'] = flask_loginVersion
versions['flask_principal'] = flask_principalVersion
versions['tornado'] = tornadoVersion
versions['iso639'] = iso639Version
versions['requests'] = requests.__version__
versions['pysqlite'] = db.engine.dialect.dbapi.version
versions['sqlite'] = db.engine.dialect.dbapi.sqlite_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")) categorycounter=categorys, seriecounter=series, title=_(u"Statistics"))
@ -1087,9 +1106,10 @@ def search():
else: else:
filter = True filter = True
entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")), entries = db.session.query(db.Books).filter(db.or_(db.Books.tags.any(db.Tags.name.like("%" + term + "%")),
db.Books.series.any(db.Series.name.like("%" + term + "%")), db.Books.series.any(db.Series.name.like("%" + term + "%")),
db.Books.authors.any(db.Authors.name.like("%" + term + "%")), db.Books.authors.any(db.Authors.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all() db.Books.publishers.any(db.Publishers.name.like("%" + term + "%")),
db.Books.title.like("%" + term + "%"))).filter(filter).all()
return render_title_template('search.html', searchterm=term, entries=entries) return render_title_template('search.html', searchterm=term, entries=entries)
else: else:
return render_title_template('search.html', searchterm="") return render_title_template('search.html', searchterm="")
@ -1109,12 +1129,14 @@ def advanced_search():
author_name = request.args.get("author_name") author_name = request.args.get("author_name")
book_title = request.args.get("book_title") book_title = request.args.get("book_title")
publisher = request.args.get("publisher")
if author_name: author_name = author_name.strip() if author_name: author_name = author_name.strip()
if book_title: book_title = book_title.strip() if book_title: book_title = book_title.strip()
if publisher: publisher = publisher.strip()
if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \ if include_tag_inputs or exclude_tag_inputs or include_series_inputs or exclude_series_inputs or \
include_languages_inputs or exclude_languages_inputs or author_name or book_title: include_languages_inputs or exclude_languages_inputs or author_name or book_title or publisher:
searchterm = [] searchterm = []
searchterm.extend((author_name, book_title)) searchterm.extend((author_name, book_title, publisher))
tag_names = db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all() tag_names = db.session.query(db.Tags).filter(db.Tags.id.in_(include_tag_inputs)).all()
searchterm.extend(tag.name for tag in tag_names) searchterm.extend(tag.name for tag in tag_names)
# searchterm = " + ".join(filter(None, searchterm)) # searchterm = " + ".join(filter(None, searchterm))
@ -1130,7 +1152,8 @@ def advanced_search():
searchterm.extend(language.name for language in language_names) searchterm.extend(language.name for language in language_names)
searchterm = " + ".join(filter(None, searchterm)) searchterm = " + ".join(filter(None, searchterm))
q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_name + "%")), q = q.filter(db.Books.authors.any(db.Authors.name.like("%" + author_name + "%")),
db.Books.title.like("%" + book_title + "%")) db.Books.title.like("%" + book_title + "%"),
db.Books.publishers.any(db.Publishers.name.like("%" + publisher + "%")))
for tag in include_tag_inputs: for tag in include_tag_inputs:
q = q.filter(db.Books.tags.any(db.Tags.id == tag)) q = q.filter(db.Books.tags.any(db.Tags.id == tag))
for tag in exclude_tag_inputs: for tag in exclude_tag_inputs:
@ -1180,7 +1203,7 @@ def feed_get_cover(book_id):
@app.route("/read/<int:book_id>/<format>") @app.route("/read/<int:book_id>/<format>")
@login_required @login_required_if_no_ano
def read_book(book_id, format): def read_book(book_id, format):
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()
if book: if book:
@ -1789,6 +1812,8 @@ def edit_mailsettings():
category="success") category="success")
else: else:
flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error") flash(_(u"There was an error sending the Test E-Mail: %(res)s", res=result), category="error")
else:
flash(_(u"E-Mail settings updated"), category="success")
return render_title_template("email_edit.html", content=content, title=_(u"Edit mail settings")) return render_title_template("email_edit.html", content=content, title=_(u"Edit mail settings"))
@ -2130,6 +2155,18 @@ def upload():
db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4())) db.session.connection().connection.connection.create_function('uuid4', 0, lambda: str(uuid4()))
if request.method == 'POST' and 'btn-upload' in request.files: if request.method == 'POST' and 'btn-upload' in request.files:
file = request.files['btn-upload'] file = request.files['btn-upload']
if '.' in file.filename:
file_ext = file.filename.rsplit('.', 1)[-1].lower()
if file_ext not in ALLOWED_EXTENSIONS:
flash(
_('File extension "%s" is not allowed to be uploaded to this server' %
file_ext),
category="error"
)
return redirect(url_for('index'))
else:
flash(_('File to be uploaded must have an extension'), category="error")
return redirect(url_for('index'))
meta = uploader.upload(file) meta = uploader.upload(file)
title = meta.title title = meta.title

Loading…
Cancel
Save