Added polish in readme to supported UI languages

Handling of missing tags in fb import
naming of path is more imitating calibre (replacement of special characters, "pinyining" of author names if unidecode is available )
Sorting of authors (similar to calibre for jr./sr./I..IV endings)
bugfix pathseparator on windows and linux during upload
bugfix os.rename for authordir
publishing date on detailview is formated according to slected locale
filename on downloading from web ui is now correct displayed
added ids to html for testing
pull/125/head^2
OzzieIsaacs 7 years ago
parent 13caa54aad
commit 93b19165cf

@ -99,7 +99,7 @@ def pdf_preview(tmp_file_path, tmp_dir):
return None return None
else: else:
cover_file_name = os.path.splitext(tmp_file_path)[0] + ".cover.jpg" cover_file_name = os.path.splitext(tmp_file_path)[0] + ".cover.jpg"
with Image(filename=tmp_file_path +"[0]", resolution=150) as img: with Image(filename=tmp_file_path + "[0]", resolution=150) as img:
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

@ -32,29 +32,29 @@ def title_sort(title):
Base = declarative_base() Base = declarative_base()
books_authors_link = Table('books_authors_link', Base.metadata, books_authors_link = Table('books_authors_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('author', Integer, ForeignKey('authors.id'), primary_key=True) Column('author', Integer, ForeignKey('authors.id'), primary_key=True)
) )
books_tags_link = Table('books_tags_link', Base.metadata, books_tags_link = Table('books_tags_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('tag', Integer, ForeignKey('tags.id'), primary_key=True) Column('tag', Integer, ForeignKey('tags.id'), primary_key=True)
) )
books_series_link = Table('books_series_link', Base.metadata, books_series_link = Table('books_series_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('series', Integer, ForeignKey('series.id'), primary_key=True) Column('series', Integer, ForeignKey('series.id'), primary_key=True)
) )
books_ratings_link = Table('books_ratings_link', Base.metadata, books_ratings_link = Table('books_ratings_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True) Column('rating', Integer, ForeignKey('ratings.id'), primary_key=True)
) )
books_languages_link = Table('books_languages_link', Base.metadata, books_languages_link = Table('books_languages_link', Base.metadata,
Column('book', Integer, ForeignKey('books.id'), primary_key=True), Column('book', Integer, ForeignKey('books.id'), primary_key=True),
Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True) Column('lang_code', Integer, ForeignKey('languages.id'), primary_key=True)
) )
class Identifiers(Base): class Identifiers(Base):
@ -227,7 +227,7 @@ class Books(Base):
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,
authors, tags): # ToDO check Authors and tags necessary authors, tags):
self.title = title self.title = title
self.sort = sort self.sort = sort
self.author_sort = author_sort self.author_sort = author_sort

@ -6,7 +6,7 @@ import os
import uploader import uploader
import StringIO import StringIO
# ToDo: Check usage of original_file_name
def get_fb2_info(tmp_file_path, original_file_extension): def get_fb2_info(tmp_file_path, original_file_extension):
ns = { ns = {
@ -20,37 +20,35 @@ def get_fb2_info(tmp_file_path, original_file_extension):
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):
last_name=element.xpath('fb:last-name/text()', namespaces=ns) last_name = element.xpath('fb:last-name/text()', namespaces=ns)
if len(last_name): if len(last_name):
last_name=last_name[0] last_name = last_name[0]
else: else:
last_name=u'' last_name = u''
middle_name=element.xpath('fb:middle-name/text()', namespaces=ns) middle_name = element.xpath('fb:middle-name/text()', namespaces=ns)
if len(middle_name): if len(middle_name):
middle_name=middle_name[0] middle_name = middle_name[0]
else: else:
middle_name=u'' middle_name = u''
first_name=element.xpath('fb:first-name/text()', namespaces=ns) first_name = element.xpath('fb:first-name/text()', namespaces=ns)
if len(first_name): if len(first_name):
first_name=first_name[0] first_name = first_name[0]
else: else:
first_name=u'' first_name = u''
return first_name + ' ' + middle_name + ' ' + last_name return first_name + ' ' + middle_name + ' ' + last_name
author = unicode(", ".join(map(get_author, authors))) author = unicode(", ".join(map(get_author, authors)))
title = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns) title = tree.xpath('/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()', namespaces=ns)
if len(title): if len(title):
title=unicode(title[0]) title = unicode(title[0])
else: else:
title=u'' title = u''
description = tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()', namespaces=ns) description = tree.xpath('/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()', namespaces=ns)
if len(description): if len(description):
description=unicode(description[0]) description = unicode(description[0])
else: else:
description=u'' description = u''
return uploader.BookMeta( return uploader.BookMeta(
file_path=tmp_file_path, file_path=tmp_file_path,

@ -22,6 +22,11 @@ from email.generator import Generator
from flask_babel import gettext as _ from flask_babel import gettext as _
import subprocess import subprocess
import shutil import shutil
try:
import unidecode
use_unidecode=True
except:
use_unidecode=False
def update_download(book_id, user_id): def update_download(book_id, user_id):
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
@ -203,7 +208,7 @@ def get_attachment(file_path):
return attachment return attachment
except IOError: except IOError:
traceback.print_exc() traceback.print_exc()
message = (_('The requested file could not be read. Maybe wrong permissions?')) # ToDo: What is this? app.logger.error = (u'The requested file could not be read. Maybe wrong permissions?')
return None return None
@ -212,47 +217,54 @@ 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
filename. Limits num characters to 128 max. filename. Limits num characters to 128 max.
""" """
value = value[:128] if value[-1:] ==u'.':
# re_slugify = re.compile('[^\w\s-]', re.UNICODE) value = value[:-1]+u'_'
value = unicodedata.normalize('NFKD', value) if use_unidecode:
re_slugify = re.compile('[^\w\s-]', re.UNICODE) value=(unidecode.unidecode(value)).strip()
value = unicode(re_slugify.sub('', value).strip()) else:
value=value.replace('§','SS')
value=value.replace('ß','ss')
value = unicodedata.normalize('NFKD', value)
re_slugify = re.compile('[\W\s-]', re.UNICODE)
value = unicode(re_slugify.sub('', value).strip())
if replace_whitespace: if replace_whitespace:
value = re.sub('[\s]+', '_', value, flags=re.U) #*+:\"/<>? werden durch _ ersetzt
value = value.replace(u"\u00DF", "ss") value = re.sub('[\*\+:\\\"/<>\?]+', '_', value, flags=re.U)
value = value[:128]
return value return value
def get_sorted_author(value):
regexes = ["^(JR|SR)\.?$","^I{1,3}\.?$","^IV\.?$"]
combined = "(" + ")|(".join(regexes) + ")"
value = value.split(" ")
if re.match(combined,value[-1].upper()):
value2 = value[-2] + ", " + " ".join(value[:-2]) + " " + value[-1]
else:
value2 = value[-1] + ", " + " ".join(value[:-1])
return value2
def get_normalized_author(value):
"""
Normalizes sorted author name
"""
value = unicodedata.normalize('NFKD', value)
value = re.sub('[^\w,\s]', '', value, flags=re.U)
value = " ".join(value.split(", ")[::-1])
return value
def update_dir_stucture(book_id, calibrepath): def update_dir_stucture(book_id, calibrepath):
db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort) db.session.connection().connection.connection.create_function("title_sort", 1, db.title_sort)
book = db.session.query(db.Books).filter(db.Books.id == book_id).first() book = db.session.query(db.Books).filter(db.Books.id == book_id).first()
path = os.path.join(calibrepath, book.path) path = os.path.join(calibrepath, book.path)#.replace('/',os.path.sep)).replace('\\',os.path.sep)
authordir = book.path.split(os.sep)[0] authordir = book.path.split('/')[0]
new_authordir = get_valid_filename(book.authors[0].name, False) new_authordir = get_valid_filename(book.authors[0].name)
titledir = book.path.split(os.sep)[1] titledir = book.path.split('/')[1]
new_titledir = get_valid_filename(book.title, False) + " (" + str(book_id) + ")" new_titledir = get_valid_filename(book.title) + " (" + str(book_id) + ")"
if titledir != new_titledir: if titledir != new_titledir:
new_title_path = os.path.join(os.path.dirname(path), new_titledir) new_title_path = os.path.join(os.path.dirname(path), new_titledir)
os.rename(path, new_title_path) os.rename(path, new_title_path)
path = new_title_path path = new_title_path
book.path = book.path.split(os.sep)[0] + os.sep + new_titledir book.path = book.path.split('/')[0] + '/' + new_titledir
if authordir != new_authordir: if authordir != new_authordir:
new_author_path = os.path.join(os.path.join(calibrepath, new_authordir), os.path.basename(path)) new_author_path = os.path.join(os.path.join(calibrepath, new_authordir), os.path.basename(path))
os.renames(path, new_author_path) os.rename(path, new_author_path)
book.path = new_authordir + os.sep + book.path.split(os.sep)[1] book.path = new_authordir + '/' + book.path.split('/')[1]
db.session.commit() db.session.commit()

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<div class="discover"> <div class="discover">
<h2>{{_('User list')}}</h2> <h2>{{_('User list')}}</h2>
<table class="table table-striped"> <table class="table table-striped" id="table_user">
<tr> <tr>
<th>{{_('Nickname')}}</th> <th>{{_('Nickname')}}</th>
<th>{{_('Email')}}</th> <th>{{_('Email')}}</th>
@ -30,9 +30,9 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</table> </table>
<div class="btn btn-default"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div> <div class="btn btn-default" id="admin_new_user"><a href="{{url_for('new_user')}}">{{_('Add new user')}}</a></div>
<h2>{{_('SMTP mail settings')}}</h2> <h2>{{_('SMTP mail settings')}}</h2>
<table class="table table-striped"> <table class="table table-striped" id="table_email">
<tr> <tr>
<th>{{_('SMTP hostname')}}</th> <th>{{_('SMTP hostname')}}</th>
<th>{{_('SMTP port')}}</th> <th>{{_('SMTP port')}}</th>
@ -51,10 +51,10 @@
</table> </table>
<div class="btn btn-default"><a href="{{url_for('edit_mailsettings')}}">{{_('Change SMTP settings')}}</a></div> <div class="btn btn-default" id="admin_edit_email"><a href="{{url_for('edit_mailsettings')}}">{{_('Change SMTP settings')}}</a></div>
<h2>{{_('Configuration')}}</h2> <h2>{{_('Configuration')}}</h2>
<table class="table table-striped"> <table class="table table-striped" id="table_configuration">
<tr> <tr>
<th>{{_('Calibre DB dir')}}</th> <th>{{_('Calibre DB dir')}}</th>
<th>{{_('Log Level')}}</th> <th>{{_('Log Level')}}</th>
@ -76,6 +76,7 @@
<div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div> <div class="btn btn-default"><a href="{{url_for('configuration')}}">{{_('Configuration')}}</a></div>
<h2>{{_('Administration')}}</h2> <h2>{{_('Administration')}}</h2>
{% if not development %} {% if not development %}
<p>{{_('Current commit timestamp')}}: {{commit}} </p>
<div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</a></div> <div class="btn btn-default" data-toggle="modal" data-target="#RestartDialog">{{_('Restart Calibre-web')}}</a></div>
<div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</a></div> <div class="btn btn-default" data-toggle="modal" data-target="#ShutdownDialog">{{_('Stop Calibre-web')}}</a></div>
<div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</a></div> <div class="btn btn-default" id="check_for_update">{{_('Check for update')}}</a></div>

@ -70,8 +70,8 @@
</div> </div>
</p> </p>
{% endif %} {% endif %}
{% if entry.pubdate != '0101-01-01 00:00:00' %} {% if entry.pubdate[:10] != '0101-01-01' %}
<p>{{_('Publishing date')}}: {{entry.pubdate[:10]}} </p> <p>{{_('Publishing date')}}: {{entry.pubdate|formatdate}} </p>
{% endif %} {% endif %}
{% if cc|length > 0 %} {% if cc|length > 0 %}
<p> <p>

@ -6,7 +6,7 @@
<div class="row"> <div class="row">
{% for entry in random %} {% for entry in random %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div id="books_rand" class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('show_book', id=entry.id) }}"> <a href="{{ url_for('show_book', id=entry.id) }}">
{% if entry.has_cover %} {% if entry.has_cover %}
@ -41,7 +41,7 @@
<h2>{{title}}</h2> <h2>{{title}}</h2>
<div class="row"> <div class="row">
{% for entry in entries %} {% for entry in entries %}
<div class="col-sm-3 col-lg-2 col-xs-6 book"> <div id="books" class="col-sm-3 col-lg-2 col-xs-6 book">
<div class="cover"> <div class="cover">
<a href="{{ url_for('show_book', id=entry.id) }}"> <a href="{{ url_for('show_book', id=entry.id) }}">
{% if entry.has_cover %} {% if entry.has_cover %}

@ -5,7 +5,7 @@
{% for lang in languages %} {% for lang in languages %}
<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 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>

@ -80,16 +80,16 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if g.user.role_admin() %} {% if g.user.role_admin() %}
<li><a href="{{url_for('admin')}}"><span class="glyphicon glyphicon-dashboard"></span> {{_('Admin')}}</a></li> <li><a id="top_admin" href="{{url_for('admin')}}"><span class="glyphicon glyphicon-dashboard"></span> {{_('Admin')}}</a></li>
{% endif %} {% endif %}
<li><a href="{{url_for('profile')}}"><span class="glyphicon glyphicon-user"></span> {{g.user.nickname}}</a></li> <li><a id="top_user" href="{{url_for('profile')}}"><span class="glyphicon glyphicon-user"></span> {{g.user.nickname}}</a></li>
{% if not g.user.is_anonymous() %} {% if not g.user.is_anonymous() %}
<li><a href="{{url_for('logout')}}"><span class="glyphicon glyphicon-log-out"></span> {{_('Logout')}}</a></li> <li><a id="logout" href="{{url_for('logout')}}"><span class="glyphicon glyphicon-log-out"></span> {{_('Logout')}}</a></li>
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if g.allow_registration and not g.user.is_authenticated %} {% if g.allow_registration and not g.user.is_authenticated %}
<li><a href="{{url_for('login')}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li> <li><a id="login" href="{{url_for('login')}}"><span class="glyphicon glyphicon-log-in"></span> {{_('Login')}}</a></li>
<li><a href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li> <li><a id="register" href="{{url_for('register')}}"><span class="glyphicon glyphicon-user"></span> {{_('Register')}}</a></li>
{% endif %} {% endif %}
</ul> </ul>
</div><!--/.nav-collapse --> </div><!--/.nav-collapse -->
@ -98,17 +98,17 @@
{% for message in get_flashed_messages(with_categories=True) %} {% for message in get_flashed_messages(with_categories=True) %}
{%if message[0] == "error" %} {%if message[0] == "error" %}
<div class="row-fluid" style="margin-top: -20px; text-align: center;"> <div class="row-fluid" style="margin-top: -20px; text-align: center;">
<div class="alert alert-danger">{{ message[1] }}</div> <div id="flash_alert" class="alert alert-danger">{{ message[1] }}</div>
</div> </div>
{%endif%} {%endif%}
{%if message[0] == "info" %} {%if message[0] == "info" %}
<div class="row-fluid" style="margin-top: -20px; text-align: center;"> <div class="row-fluid" style="margin-top: -20px; text-align: center;">
<div class="alert alert-info">{{ message[1] }}</div> <div id="flash_info" class="alert alert-info">{{ message[1] }}</div>
</div> </div>
{%endif%} {%endif%}
{%if message[0] == "success" %} {%if message[0] == "success" %}
<div class="row-fluid" style="margin-top: -20px; text-align: center;"> <div class="row-fluid" style="margin-top: -20px; text-align: center;">
<div class="alert alert-success">{{ message[1] }}</div> <div id="flash_success" class="alert alert-success">{{ message[1] }}</div>
</div> </div>
{%endif%} {%endif%}
{% endfor %} {% endfor %}
@ -119,25 +119,25 @@
<nav class="navigation"> <nav class="navigation">
<ul class="list-unstyled" id="scnd-nav" intent in-standard-append="nav.navigation" in-mobile-after="#main-nav" in-mobile-class="nav navbar-nav"> <ul class="list-unstyled" id="scnd-nav" intent in-standard-append="nav.navigation" in-mobile-after="#main-nav" in-mobile-class="nav navbar-nav">
<li class="nav-head hidden-xs">{{_('Browse')}}</li> <li class="nav-head hidden-xs">{{_('Browse')}}</li>
<li><a href="{{url_for('index')}}"><span class="glyphicon glyphicon-book"></span> {{_('New Books')}}</a></li> <li id="nav_new"><a href="{{url_for('index')}}"><span class="glyphicon glyphicon-book"></span> {{_('New Books')}}</a></li>
{% if g.user.show_hot_books() %} {% if g.user.show_hot_books() %}
<li><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span> {{_('Hot Books')}}</a></li> <li id="nav_hot"><a href="{{url_for('hot_books')}}"><span class="glyphicon glyphicon-fire"></span> {{_('Hot Books')}}</a></li>
{%endif%} {%endif%}
{% if g.user.show_best_rated_books() %} {% if g.user.show_best_rated_books() %}
<li><a href="{{url_for('best_rated_books')}}"><span class="glyphicon glyphicon-star"></span> {{_('Best rated Books')}}</a></li> <li><a href="{{url_for('best_rated_books')}}"><span class="glyphicon glyphicon-star"></span> {{_('Best rated Books')}}</a></li>
{%endif%} {%endif%}
{% if g.user.show_random_books() %} {% if g.user.show_random_books() %}
<li><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> {{_('Discover')}}</a></li> <li id="nav_rand"><a href="{{url_for('discover')}}"><span class="glyphicon glyphicon-random"></span> {{_('Discover')}}</a></li>
{%endif%} {%endif%}
{% if g.user.show_category() %} {% if g.user.show_category() %}
<li><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span> {{_('Categories')}}</a></li> <li id="nav_cat"><a href="{{url_for('category_list')}}"><span class="glyphicon glyphicon-inbox"></span> {{_('Categories')}}</a></li>
{%endif%} {%endif%}
{% if g.user.show_series() %} {% if g.user.show_series() %}
<li><a href="{{url_for('series_list')}}"><span class="glyphicon glyphicon-bookmark"></span> {{_('Series')}}</a></li> <li id="nav_serie"><a href="{{url_for('series_list')}}"><span class="glyphicon glyphicon-bookmark"></span> {{_('Series')}}</a></li>
{%endif%} {%endif%}
<li><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span> {{_('Authors')}}</a></li> <li id="nav_author"><a href="{{url_for('author_list')}}"><span class="glyphicon glyphicon-user"></span> {{_('Authors')}}</a></li>
{% if g.user.filter_language() == 'all' and g.user.show_language() %} {% if g.user.filter_language() == 'all' and g.user.show_language() %}
<li><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li> <li id="nav_lang"><a href="{{url_for('language_overview')}}"><span class="glyphicon glyphicon-flag"></span> {{_('Languages')}} </a></li>
{%endif%} {%endif%}
{% if g.user.is_authenticated or g.user.is_anonymous() %} {% if g.user.is_authenticated or g.user.is_anonymous() %}
<li class="nav-head hidden-xs">{{_('Public Shelves')}}</li> <li class="nav-head hidden-xs">{{_('Public Shelves')}}</li>
@ -160,9 +160,9 @@
{% endif %} {% endif %}
<div class="col-sm-10"> <div class="col-sm-10">
{% block body %}{% endblock %} {% block body %}{% endblock %}
{% if pagination %} {% if pagination and (pagination.has_next or pagination.has_prev) %}
<div class="pagination"> <div class="pagination">
{%- for page in pagination.iter_pages() %} {% for page in pagination.iter_pages() %}
{% if page %} {% if page %}
{% if page != pagination.page %} {% if page != pagination.page %}
<a href="{{ url_for_other_page(page) }}">{{ page }}</a> <a href="{{ url_for_other_page(page) }}">{{ page }}</a>
@ -172,7 +172,7 @@
{% else %} {% else %}
<span class="ellipsis"></span> <span class="ellipsis"></span>
{% endif %} {% endif %}
{%- endfor %} {% endfor %}
{% if pagination.has_next %} {% if pagination.has_next %}
<a class="next" href="{{ url_for_other_page(pagination.page + 1) <a class="next" href="{{ url_for_other_page(pagination.page + 1)
}}">Next &raquo;</a> }}">Next &raquo;</a>

@ -5,7 +5,7 @@
{% for entry in entries %} {% for entry in entries %}
<div class="row"> <div class="row">
<div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div> <div class="col-xs-1" align="left"><span class="badge">{{entry.count}}</span></div>
<div class="col-xs-6"><a href="{{url_for(folder, 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>

@ -16,7 +16,7 @@
<input type="checkbox" name="remember_me" checked> {{_('Remember me')}} <input type="checkbox" name="remember_me" checked> {{_('Remember me')}}
</label> </label>
</div> </div>
<button type="submit" class="btn btn-default">{{_('Submit')}}</button> <button type="submit" name="submit" class="btn btn-default">{{_('Submit')}}</button>
</form> </form>
</div> </div>
{% if error %} {% if error %}

@ -2,7 +2,7 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"> <OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
<LongName>{{instance}}</LongName> <LongName>{{instance}}</LongName>
<ShortName>{{instance}}</ShortName> <ShortName>{{instance}}</ShortName>
<Description>{{_('instanceCalibre Web ebook catalog')}}</Description> <Description>{{_('Calibre Web ebook catalog')}}</Description>
<Developer>Janeczku</Developer> <Developer>Janeczku</Developer>
<Contact>https://github.com/janeczku/calibre-web</Contact> <Contact>https://github.com/janeczku/calibre-web</Contact>
<Url type="text/html" <Url type="text/html"

@ -2,7 +2,7 @@
{% block body %} {% block body %}
<h3>{{_('Linked libraries')}}</h3> <h3>{{_('Linked libraries')}}</h3>
<table class="table"> <table id="libs" class="table">
<thead> <thead>
<tr> <tr>
<th>{{_('Program library')}}</th> <th>{{_('Program library')}}</th>
@ -30,7 +30,7 @@
</table> </table>
<h3>{{_('Calibre library statistics')}}</h3> <h3>{{_('Calibre library statistics')}}</h3>
<table class="table"> <table id="stats" class="table">
<tbody> <tbody>
<tr> <tr>
<th>{{bookcounter}}</th> <th>{{bookcounter}}</th>

@ -27,7 +27,7 @@
<label for="locale">{{_('Language')}}</label> <label for="locale">{{_('Language')}}</label>
<select name="locale" id="locale" class="form-control"> <select name="locale" id="locale" class="form-control">
{% for translation in translations %} {% for translation in translations %}
<option value="{{translation}}" {% if translation.language == content.locale %}selected{% endif %} {% if new_user == 1 and loop.first %}selected{% endif %}>{{ translation.display_name }}</option> <option value="{{translation}}" {% if translation|string == content.locale %}selected{% endif %} {% if new_user == 1 and loop.first %}selected{% endif %}>{{ translation.display_name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
@ -108,7 +108,7 @@
{% endif %} {% endif %}
<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') }}" class="btn btn-default">{{_('Back')}}</a> <a href="{{ url_for('admin') }}" id="back" class="btn btn-default">{{_('Back')}}</a>
{% endif %} {% endif %}
</form> </form>

@ -81,7 +81,7 @@ msgstr "Beliebte Bücher (die meisten Downloads)"
#: cps/web.py:813 #: cps/web.py:813
msgid "Best rated books" msgid "Best rated books"
msgstr "" msgstr "Best bewertete Bücher"
#: cps/templates/index.xml:36 cps/web.py:822 #: cps/templates/index.xml:36 cps/web.py:822
msgid "Random Books" msgid "Random Books"
@ -94,7 +94,7 @@ msgstr "Autorenliste"
#: cps/web.py:846 #: cps/web.py:846
#, python-format #, python-format
msgid "Author: %(name)s" msgid "Author: %(name)s"
msgstr "" msgstr "Autor: %(name)s"
#: cps/web.py:848 cps/web.py:876 cps/web.py:975 cps/web.py:1216 cps/web.py:2103 #: cps/web.py:848 cps/web.py:876 cps/web.py:975 cps/web.py:1216 cps/web.py:2103
msgid "Error opening eBook. File does not exist or file is not accessible:" msgid "Error opening eBook. File does not exist or file is not accessible:"
@ -143,7 +143,7 @@ msgstr "Server wird runtergefahren, bitte Fenster schließen"
#: cps/web.py:1055 #: cps/web.py:1055
msgid "Update done" msgid "Update done"
msgstr "" msgstr "Update durchgeführt"
#: cps/web.py:1128 cps/web.py:1141 #: cps/web.py:1128 cps/web.py:1141
msgid "search" msgid "search"
@ -470,11 +470,11 @@ msgstr "Stoppe Calibre-web"
#: cps/templates/admin.html:81 #: cps/templates/admin.html:81
msgid "Check for update" msgid "Check for update"
msgstr "" msgstr "Suche nach Update"
#: cps/templates/admin.html:82 #: cps/templates/admin.html:82
msgid "Perform Update" msgid "Perform Update"
msgstr "" msgstr "Update durchführen"
#: cps/templates/admin.html:93 #: cps/templates/admin.html:93
msgid "Do you really want to restart Calibre-web?" msgid "Do you really want to restart Calibre-web?"
@ -584,7 +584,7 @@ msgstr "Öffentliche Registrierung aktivieren"
#: 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 "Default Einstellungen für neue Benutzer"
#: 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"
@ -625,7 +625,7 @@ msgstr "Sprache"
#: cps/templates/detail.html:74 #: cps/templates/detail.html:74
msgid "Publishing date" msgid "Publishing date"
msgstr "" msgstr "Herausgabedatum"
#: cps/templates/detail.html:106 #: cps/templates/detail.html:106
msgid "Description:" msgid "Description:"
@ -699,11 +699,11 @@ msgstr "Beliebte Bücher"
#: 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 "Beliebte Publikationen aus dieser Bibliothek basierend auf Downloadzahlen"
#: cps/templates/index.xml:22 cps/templates/layout.html:127 #: cps/templates/index.xml:22 cps/templates/layout.html:127
msgid "Best rated Books" msgid "Best rated Books"
msgstr "" msgstr "Best bewertete Bücher"
#: 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."
@ -804,8 +804,8 @@ msgid "Remember me"
msgstr "Merken" msgstr "Merken"
#: cps/templates/osd.xml:5 #: cps/templates/osd.xml:5
msgid "instanceCalibre Web ebook catalog" msgid "Calibre Web ebook catalog"
msgstr "" msgstr "Calibre Web Ebook Katalog"
#: cps/templates/read.html:136 #: cps/templates/read.html:136
msgid "Reflow text when sidebars are open." msgid "Reflow text when sidebars are open."
@ -909,11 +909,11 @@ msgstr "Autoren in dieser Bibliothek"
#: cps/templates/stats.html:45 #: cps/templates/stats.html:45
msgid "Categories in this Library" msgid "Categories in this Library"
msgstr "" msgstr "Kategorien in dieser Bibliothek"
#: cps/templates/stats.html:49 #: cps/templates/stats.html:49
msgid "Series in this Library" msgid "Series in this Library"
msgstr "" msgstr "Serien in dieser Bibliothek"
#: cps/templates/user_edit.html:23 #: cps/templates/user_edit.html:23
msgid "Kindle E-Mail" msgid "Kindle E-Mail"
@ -937,7 +937,7 @@ msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:53 #: cps/templates/user_edit.html:53
msgid "Show best rated books" msgid "Show best rated books"
msgstr "" msgstr "Zeige am besten bewertete Bücher"
#: cps/templates/user_edit.html:57 #: cps/templates/user_edit.html:57
msgid "Show language selection" msgid "Show language selection"

@ -144,7 +144,6 @@ class UserBase:
else: else:
return False return False
def __repr__(self): def __repr__(self):
return '<User %r>' % self.nickname return '<User %r>' % self.nickname
@ -164,10 +163,6 @@ class User(UserBase, Base):
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")
sidebar_view = Column(Integer, default=1) sidebar_view = Column(Integer, default=1)
#language_books = Column(Integer, default=1)
#series_books = Column(Integer, default=1)
#category_books = Column(Integer, default=1)
#hot_books = Column(Integer, default=1)
default_language = Column(String(3), default="all") default_language = Column(String(3), default="all")
@ -184,10 +179,6 @@ class Anonymous(AnonymousUserMixin, UserBase):
self.role = data.role self.role = data.role
self.sidebar_view = data.sidebar_view self.sidebar_view = data.sidebar_view
self.default_language = data.default_language self.default_language = data.default_language
#self.language_books = data.language_books
#self.series_books = data.series_books
#self.category_books = data.category_books
#self.hot_books = data.hot_books
self.default_language = data.default_language self.default_language = data.default_language
self.locale = data.locale self.locale = data.locale
self.anon_browse = settings.config_anonbrowse self.anon_browse = settings.config_anonbrowse

@ -25,6 +25,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.dates import format_date
from functools import wraps from functools import wraps
import base64 import base64
from sqlalchemy.sql import * from sqlalchemy.sql import *
@ -279,6 +280,12 @@ def mimetype_filter(val):
s = 'application/octet-stream' s = 'application/octet-stream'
return s return s
@app.template_filter('formatdate')
def formatdate(val):
conformed_timestamp = re.sub(r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))", '', val)
formatdate = datetime.datetime.strptime(conformed_timestamp[:-5], "%Y%m%d %H%M%S")
return format_date(formatdate, format='medium',locale=get_locale())
def admin_required(f): def admin_required(f):
""" """
@ -658,10 +665,9 @@ def get_opds_download_link(book_id, format):
data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == format.upper()).first() data = db.session.query(db.Data).filter(db.Data.book == book.id).filter(db.Data.format == format.upper()).first()
if current_user.is_authenticated: if current_user.is_authenticated:
helper.update_download(book_id, int(current_user.id)) helper.update_download(book_id, int(current_user.id))
author = helper.get_normalized_author(book.author_sort)
file_name = book.title file_name = book.title
if len(author) > 0: if len(book.authors) > 0:
file_name = author + '-' + file_name file_name = book.authors[0].name + '-' + file_name
file_name = helper.get_valid_filename(file_name) file_name = helper.get_valid_filename(file_name)
response = make_response(send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format)) response = make_response(send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format))
response.headers["Content-Disposition"] = "attachment; filename=\"%s.%s\"" % (data.name, format) response.headers["Content-Disposition"] = "attachment; filename=\"%s.%s\"" % (data.name, format)
@ -1228,10 +1234,9 @@ def get_download_link(book_id, format):
# collect downloaded books only for registered user and not for anonymous user # collect downloaded books only for registered user and not for anonymous user
if current_user.is_authenticated: if current_user.is_authenticated:
helper.update_download(book_id, int(current_user.id)) helper.update_download(book_id, int(current_user.id))
author = helper.get_normalized_author(book.author_sort)
file_name = book.title file_name = book.title
if len(author) > 0: if len(book.authors) > 0:
file_name = author + '-' + file_name file_name = book.authors[0].name + '-' + file_name
file_name = helper.get_valid_filename(file_name) file_name = helper.get_valid_filename(file_name)
response = make_response( response = make_response(
send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format)) send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + format))
@ -1239,13 +1244,7 @@ def get_download_link(book_id, format):
response.headers["Content-Type"] = mimetypes.types_map['.' + format] response.headers["Content-Type"] = mimetypes.types_map['.' + format]
except: except:
pass pass
response.headers["Content-Disposition"] = \ response.headers["Content-Disposition"] = "attachment; filename=\"%s.%s\"" % (file_name.encode('utf-8'), format)
"attachment; " \
"filename={utf_filename}.{suffix};" \
"filename*=UTF-8''{utf_filename}.{suffix}".format(
utf_filename=file_name.encode('utf-8'),
suffix=format
)
return response return response
else: else:
abort(404) abort(404)
@ -1599,6 +1598,7 @@ def basic_configuration():
def configuration_helper(origin): def configuration_helper(origin):
global global_task global global_task
commit='$Format:%cI$'
reboot_required = False reboot_required = False
db_change = False db_change = False
success = False success = False
@ -1659,16 +1659,16 @@ def configuration_helper(origin):
logging.getLogger("book_formats").setLevel(config.config_log_level) logging.getLogger("book_formats").setLevel(config.config_log_level)
except e: except e:
flash(e, category="error") flash(e, category="error")
return render_title_template("config_edit.html", content=config, origin=origin, return render_title_template("config_edit.html", content=config, origin=origin, commit=commit,
title=_(u"Basic Configuration")) title=_(u"Basic Configuration"))
if db_change: if db_change:
reload(db) reload(db)
if not db.setup_db(): if not db.setup_db():
flash(_(u'DB location is not valid, please enter correct path'), category="error") flash(_(u'DB location is not valid, please enter correct path'), category="error")
return render_title_template("config_edit.html", content=config, origin=origin, return render_title_template("config_edit.html", content=config, origin=origin, commit=commit,
title=_(u"Basic Configuration")) title=_(u"Basic Configuration"))
if reboot_required: if reboot_required:
# db.engine.dispose() # ToDo verify correct # db.engine.dispose() # ToDo verify correct
ub.session.close() ub.session.close()
ub.engine.dispose() ub.engine.dispose()
# stop tornado server # stop tornado server
@ -1678,7 +1678,7 @@ def configuration_helper(origin):
app.logger.info('Reboot required, restarting') app.logger.info('Reboot required, restarting')
if origin: if origin:
success = True success = True
return render_title_template("config_edit.html", origin=origin, success=success, content=config, return render_title_template("config_edit.html", origin=origin, success=success, content=config, commit=commit,
title=_(u"Basic Configuration")) title=_(u"Basic Configuration"))
@ -1927,7 +1927,7 @@ def edit_book(book_id):
modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author') modify_database_object(input_authors, book.authors, db.Authors, db.session, 'author')
if author0_before_edit != book.authors[0].name: if author0_before_edit != book.authors[0].name:
edited_books_id.add(book.id) edited_books_id.add(book.id)
book.author_sort=helper.get_normalized_author(input_authors[0]) # ToDo: wrong sorting book.author_sort=helper.get_sorted_author(input_authors[0])
if to_save["cover_url"] and os.path.splitext(to_save["cover_url"])[1].lower() == ".jpg": if to_save["cover_url"] and os.path.splitext(to_save["cover_url"])[1].lower() == ".jpg":
img = requests.get(to_save["cover_url"]) img = requests.get(to_save["cover_url"])
@ -2155,9 +2155,10 @@ def upload():
if is_author: if is_author:
db_author = is_author db_author = is_author
else: else:
db_author = db.Authors(author, helper.get_normalized_author(author), "") # TODO: WRONG Sorting Author function db_author = db.Authors(author, helper.get_sorted_author(author), "")
db.session.add(db_author) db.session.add(db_author)
path = os.path.join(author_dir, title_dir) # combine path and normalize path from windows systems
path = os.path.join(author_dir, title_dir).replace('\\','/')
db_book = db.Books(title, "", db_author.sort, datetime.datetime.now(), datetime.datetime(101, 01, 01), 1, db_book = db.Books(title, "", db_author.sort, datetime.datetime.now(), datetime.datetime(101, 01, 01), 1,
datetime.datetime.now(), path, has_cover, db_author, []) datetime.datetime.now(), path, has_cover, db_author, [])
db_book.authors.append(db_author) db_book.authors.append(db_author)

@ -11,7 +11,7 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
- full graphical setup - full graphical setup
- User management - User management
- Admin interface - Admin interface
- User Interface in english, french, german, simplified chinese, spanish - User Interface in english, french, german, polish, simplified chinese, spanish
- OPDS feed for eBook reader apps - OPDS feed for eBook reader apps
- Filter and search by titles, authors, tags, series and language - Filter and search by titles, authors, tags, series and language
- Create custom book collection (shelves) - Create custom book collection (shelves)

Loading…
Cancel
Save