mirror of
https://github.com/janeczku/calibre-web
synced 2024-11-28 12:30:00 +00:00
Merge branch 'janeczku-master'
This commit is contained in:
commit
42eea35682
@ -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
|
||||||
|
@ -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
|
||||||
|
30
cps/fb2.py
30
cps/fb2.py
@ -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'_'
|
||||||
|
if use_unidecode:
|
||||||
|
value=(unidecode.unidecode(value)).strip()
|
||||||
|
else:
|
||||||
|
value=value.replace('§','SS')
|
||||||
|
value=value.replace('ß','ss')
|
||||||
value = unicodedata.normalize('NFKD', value)
|
value = unicodedata.normalize('NFKD', value)
|
||||||
re_slugify = re.compile('[^\w\s-]', re.UNICODE)
|
re_slugify = re.compile('[\W\s-]', re.UNICODE)
|
||||||
value = unicode(re_slugify.sub('', value).strip())
|
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):
|
||||||
def get_normalized_author(value):
|
regexes = ["^(JR|SR)\.?$","^I{1,3}\.?$","^IV\.?$"]
|
||||||
"""
|
combined = "(" + ")|(".join(regexes) + ")"
|
||||||
Normalizes sorted author name
|
value = value.split(" ")
|
||||||
"""
|
if re.match(combined,value[-1].upper()):
|
||||||
value = unicodedata.normalize('NFKD', value)
|
value2 = value[-2] + ", " + " ".join(value[:-2]) + " " + value[-1]
|
||||||
value = re.sub('[^\w,\s]', '', value, flags=re.U)
|
else:
|
||||||
value = " ".join(value.split(", ")[::-1])
|
value2 = value[-1] + ", " + " ".join(value[:-1])
|
||||||
return value
|
return value2
|
||||||
|
|
||||||
|
|
||||||
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 »</a>
|
}}">Next »</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
|
||||||
|
35
cps/web.py
35
cps/web.py
@ -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 *
|
||||||
@ -280,6 +281,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):
|
||||||
"""
|
"""
|
||||||
@ -659,10 +666,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)
|
||||||
@ -1229,10 +1235,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))
|
||||||
@ -1240,13 +1245,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)
|
||||||
@ -1579,9 +1578,10 @@ def profile():
|
|||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin():
|
def admin():
|
||||||
|
commit = '$Format:%cI$'
|
||||||
content = ub.session.query(ub.User).all()
|
content = ub.session.query(ub.User).all()
|
||||||
settings = ub.session.query(ub.Settings).first()
|
settings = ub.session.query(ub.Settings).first()
|
||||||
return render_title_template("admin.html", content=content, email=settings, config=config,
|
return render_title_template("admin.html", content=content, email=settings, config=config, commit=commit,
|
||||||
development=ub.DEVELOPMENT, title=_(u"Admin page"))
|
development=ub.DEVELOPMENT, title=_(u"Admin page"))
|
||||||
|
|
||||||
|
|
||||||
@ -1928,7 +1928,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"])
|
||||||
@ -2168,9 +2168,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…
Reference in New Issue
Block a user