Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in / Register
Toggle navigation
D
douban-api-proxy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
captainwong
douban-api-proxy
Commits
42eea356
Commit
42eea356
authored
Feb 17, 2017
by
idalin
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'janeczku-master'
parents
e02a9152
ed459ddb
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
139 additions
and
136 deletions
+139
-136
book_formats.py
cps/book_formats.py
+1
-1
db.py
cps/db.py
+16
-16
fb2.py
cps/fb2.py
+15
-17
helper.py
cps/helper.py
+37
-25
admin.html
cps/templates/admin.html
+6
-5
detail.html
cps/templates/detail.html
+2
-2
index.html
cps/templates/index.html
+2
-2
languages.html
cps/templates/languages.html
+1
-1
layout.html
cps/templates/layout.html
+18
-18
list.html
cps/templates/list.html
+1
-1
login.html
cps/templates/login.html
+1
-1
osd.xml
cps/templates/osd.xml
+1
-1
stats.html
cps/templates/stats.html
+2
-2
user_edit.html
cps/templates/user_edit.html
+2
-2
messages.po
cps/translations/de/LC_MESSAGES/messages.po
+14
-14
ub.py
cps/ub.py
+0
-9
web.py
cps/web.py
+19
-18
readme.md
readme.md
+1
-1
No files found.
cps/book_formats.py
View file @
42eea356
...
...
@@ -99,7 +99,7 @@ def pdf_preview(tmp_file_path, tmp_dir):
return
None
else
:
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
.
save
(
filename
=
os
.
path
.
join
(
tmp_dir
,
cover_file_name
))
return
cover_file_name
...
...
cps/db.py
View file @
42eea356
...
...
@@ -32,29 +32,29 @@ def title_sort(title):
Base
=
declarative_base
()
books_authors_link
=
Table
(
'books_authors_link'
,
Base
.
metadata
,
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'author'
,
Integer
,
ForeignKey
(
'authors.id'
),
primary_key
=
True
)
)
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'author'
,
Integer
,
ForeignKey
(
'authors.id'
),
primary_key
=
True
)
)
books_tags_link
=
Table
(
'books_tags_link'
,
Base
.
metadata
,
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'tag'
,
Integer
,
ForeignKey
(
'tags.id'
),
primary_key
=
True
)
)
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'tag'
,
Integer
,
ForeignKey
(
'tags.id'
),
primary_key
=
True
)
)
books_series_link
=
Table
(
'books_series_link'
,
Base
.
metadata
,
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'series'
,
Integer
,
ForeignKey
(
'series.id'
),
primary_key
=
True
)
)
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'series'
,
Integer
,
ForeignKey
(
'series.id'
),
primary_key
=
True
)
)
books_ratings_link
=
Table
(
'books_ratings_link'
,
Base
.
metadata
,
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'rating'
,
Integer
,
ForeignKey
(
'ratings.id'
),
primary_key
=
True
)
)
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'rating'
,
Integer
,
ForeignKey
(
'ratings.id'
),
primary_key
=
True
)
)
books_languages_link
=
Table
(
'books_languages_link'
,
Base
.
metadata
,
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'lang_code'
,
Integer
,
ForeignKey
(
'languages.id'
),
primary_key
=
True
)
)
Column
(
'book'
,
Integer
,
ForeignKey
(
'books.id'
),
primary_key
=
True
),
Column
(
'lang_code'
,
Integer
,
ForeignKey
(
'languages.id'
),
primary_key
=
True
)
)
class
Identifiers
(
Base
):
...
...
@@ -227,7 +227,7 @@ class Books(Base):
identifiers
=
relationship
(
'Identifiers'
,
backref
=
'books'
)
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
.
sort
=
sort
self
.
author_sort
=
author_sort
...
...
cps/fb2.py
View file @
42eea356
...
...
@@ -6,7 +6,7 @@ import os
import
uploader
import
StringIO
# ToDo: Check usage of original_file_name
def
get_fb2_info
(
tmp_file_path
,
original_file_extension
):
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
)
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
):
last_name
=
last_name
[
0
]
last_name
=
last_name
[
0
]
else
:
last_name
=
u''
middle_name
=
element
.
xpath
(
'fb:middle-name/text()'
,
namespaces
=
ns
)
last_name
=
u''
middle_name
=
element
.
xpath
(
'fb:middle-name/text()'
,
namespaces
=
ns
)
if
len
(
middle_name
):
middle_name
=
middle_name
[
0
]
middle_name
=
middle_name
[
0
]
else
:
middle_name
=
u''
first_name
=
element
.
xpath
(
'fb:first-name/text()'
,
namespaces
=
ns
)
middle_name
=
u''
first_name
=
element
.
xpath
(
'fb:first-name/text()'
,
namespaces
=
ns
)
if
len
(
first_name
):
first_name
=
first_name
[
0
]
first_name
=
first_name
[
0
]
else
:
first_name
=
u''
return
first_name
+
' '
+
middle_name
+
' '
+
last_name
first_name
=
u''
return
first_name
+
' '
+
middle_name
+
' '
+
last_name
author
=
unicode
(
", "
.
join
(
map
(
get_author
,
authors
)))
title
=
tree
.
xpath
(
'/fb:FictionBook/fb:description/fb:title-info/fb:book-title/text()'
,
namespaces
=
ns
)
if
len
(
title
):
title
=
unicode
(
title
[
0
])
title
=
unicode
(
title
[
0
])
else
:
title
=
u''
title
=
u''
description
=
tree
.
xpath
(
'/fb:FictionBook/fb:description/fb:publish-info/fb:book-name/text()'
,
namespaces
=
ns
)
if
len
(
description
):
description
=
unicode
(
description
[
0
])
description
=
unicode
(
description
[
0
])
else
:
description
=
u''
description
=
u''
return
uploader
.
BookMeta
(
file_path
=
tmp_file_path
,
...
...
cps/helper.py
View file @
42eea356
...
...
@@ -22,6 +22,11 @@ from email.generator import Generator
from
flask_babel
import
gettext
as
_
import
subprocess
import
shutil
try
:
import
unidecode
use_unidecode
=
True
except
:
use_unidecode
=
False
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
==
...
...
@@ -203,7 +208,7 @@ def get_attachment(file_path):
return
attachment
except
IOError
:
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
...
...
@@ -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
filename. Limits num characters to 128 max.
"""
value
=
value
[:
128
]
# re_slugify = re.compile('[^\w\s-]', re.UNICODE)
value
=
unicodedata
.
normalize
(
'NFKD'
,
value
)
re_slugify
=
re
.
compile
(
'[^
\
w
\
s-]'
,
re
.
UNICODE
)
value
=
unicode
(
re_slugify
.
sub
(
''
,
value
)
.
strip
())
if
value
[
-
1
:]
==
u'.'
:
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
)
re_slugify
=
re
.
compile
(
'[
\
W
\
s-]'
,
re
.
UNICODE
)
value
=
unicode
(
re_slugify
.
sub
(
''
,
value
)
.
strip
())
if
replace_whitespace
:
value
=
re
.
sub
(
'[
\
s]+'
,
'_'
,
value
,
flags
=
re
.
U
)
value
=
value
.
replace
(
u"
\u00DF
"
,
"ss"
)
#*+:\"/<>? werden durch _ ersetzt
value
=
re
.
sub
(
'[
\
*
\
+:
\\\"
/<>
\
?]+'
,
'_'
,
value
,
flags
=
re
.
U
)
value
=
value
[:
128
]
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
):
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
()
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
]
new_authordir
=
get_valid_filename
(
book
.
authors
[
0
]
.
name
,
False
)
titledir
=
book
.
path
.
split
(
os
.
sep
)[
1
]
new_titledir
=
get_valid_filename
(
book
.
title
,
False
)
+
" ("
+
str
(
book_id
)
+
")"
authordir
=
book
.
path
.
split
(
'/'
)[
0
]
new_authordir
=
get_valid_filename
(
book
.
authors
[
0
]
.
name
)
titledir
=
book
.
path
.
split
(
'/'
)[
1
]
new_titledir
=
get_valid_filename
(
book
.
title
)
+
" ("
+
str
(
book_id
)
+
")"
if
titledir
!=
new_titledir
:
new_title_path
=
os
.
path
.
join
(
os
.
path
.
dirname
(
path
),
new_titledir
)
os
.
rename
(
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
:
new_author_path
=
os
.
path
.
join
(
os
.
path
.
join
(
calibrepath
,
new_authordir
),
os
.
path
.
basename
(
path
))
os
.
rename
s
(
path
,
new_author_path
)
book
.
path
=
new_authordir
+
os
.
sep
+
book
.
path
.
split
(
os
.
sep
)[
1
]
os
.
rename
(
path
,
new_author_path
)
book
.
path
=
new_authordir
+
'/'
+
book
.
path
.
split
(
'/'
)[
1
]
db
.
session
.
commit
()
...
...
cps/templates/admin.html
View file @
42eea356
...
...
@@ -2,7 +2,7 @@
{% block body %}
<div
class=
"discover"
>
<h2>
{{_('User list')}}
</h2>
<table
class=
"table table-striped"
>
<table
class=
"table table-striped"
id=
"table_user"
>
<tr>
<th>
{{_('Nickname')}}
</th>
<th>
{{_('Email')}}
</th>
...
...
@@ -30,9 +30,9 @@
{% endif %}
{% endfor %}
</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>
<table
class=
"table table-striped"
>
<table
class=
"table table-striped"
id=
"table_email"
>
<tr>
<th>
{{_('SMTP hostname')}}
</th>
<th>
{{_('SMTP port')}}
</th>
...
...
@@ -51,10 +51,10 @@
</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>
<table
class=
"table table-striped"
>
<table
class=
"table table-striped"
id=
"table_configuration"
>
<tr>
<th>
{{_('Calibre DB dir')}}
</th>
<th>
{{_('Log Level')}}
</th>
...
...
@@ -76,6 +76,7 @@
<div
class=
"btn btn-default"
><a
href=
"{{url_for('configuration')}}"
>
{{_('Configuration')}}
</a></div>
<h2>
{{_('Administration')}}
</h2>
{% 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=
"#ShutdownDialog"
>
{{_('Stop Calibre-web')}}
</a></div>
<div
class=
"btn btn-default"
id=
"check_for_update"
>
{{_('Check for update')}}
</a></div>
...
...
cps/templates/detail.html
View file @
42eea356
...
...
@@ -70,8 +70,8 @@
</div>
</p>
{% endif %}
{% if entry.pubdate
!= '0101-01-01 00:00:00
' %}
<p>
{{_('Publishing date')}}: {{entry.pubdate
[:10]
}}
</p>
{% if entry.pubdate
[:10] != '0101-01-01
' %}
<p>
{{_('Publishing date')}}: {{entry.pubdate
|formatdate
}}
</p>
{% endif %}
{% if cc|length > 0 %}
<p>
...
...
cps/templates/index.html
View file @
42eea356
...
...
@@ -6,7 +6,7 @@
<div
class=
"row"
>
{% 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"
>
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
{% if entry.has_cover %}
...
...
@@ -41,7 +41,7 @@
<h2>
{{title}}
</h2>
<div
class=
"row"
>
{% 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"
>
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
{% if entry.has_cover %}
...
...
cps/templates/languages.html
View file @
42eea356
...
...
@@ -5,7 +5,7 @@
{% for lang in languages %}
<div
class=
"row"
>
<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>
{% endfor %}
</div>
...
...
cps/templates/layout.html
View file @
42eea356
...
...
@@ -80,16 +80,16 @@
{% endif %}
{% endif %}
{% 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 %}
<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() %}
<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 %}
{% 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
href=
"{{url_for('register')}}"
><span
class=
"glyphicon glyphicon-user"
></span>
{{_('Register')}}
</a></li>
<li><a
id=
"login"
href=
"{{url_for('login')}}"
><span
class=
"glyphicon glyphicon-log-in"
></span>
{{_('Login')}}
</a></li>
<li><a
id=
"register"
href=
"{{url_for('register')}}"
><span
class=
"glyphicon glyphicon-user"
></span>
{{_('Register')}}
</a></li>
{% endif %}
</ul>
</div>
<!--/.nav-collapse -->
...
...
@@ -98,17 +98,17 @@
{% for message in get_flashed_messages(with_categories=True) %}
{%if message[0] == "error" %}
<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>
{%endif%}
{%if message[0] == "info" %}
<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>
{%endif%}
{%if message[0] == "success" %}
<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>
{%endif%}
{% endfor %}
...
...
@@ -119,25 +119,25 @@
<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"
>
<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() %}
<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%}
{% 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>
{%endif%}
{% 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%}
{% 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%}
{% 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%}
<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() %}
<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%}
{% if g.user.is_authenticated or g.user.is_anonymous() %}
<li
class=
"nav-head hidden-xs"
>
{{_('Public Shelves')}}
</li>
...
...
@@ -160,9 +160,9 @@
{% endif %}
<div
class=
"col-sm-10"
>
{% block body %}{% endblock %}
{% if pagination %}
{% if pagination
and (pagination.has_next or pagination.has_prev)
%}
<div
class=
"pagination"
>
{%
-
for page in pagination.iter_pages() %}
{% for page in pagination.iter_pages() %}
{% if page %}
{% if page != pagination.page %}
<a
href=
"{{ url_for_other_page(page) }}"
>
{{ page }}
</a>
...
...
@@ -172,7 +172,7 @@
{% else %}
<span
class=
"ellipsis"
>
…
</span>
{% endif %}
{%
-
endfor %}
{% endfor %}
{% if pagination.has_next %}
<a
class=
"next"
href=
"{{ url_for_other_page(pagination.page + 1)
}}"
>
Next
»
</a>
...
...
cps/templates/list.html
View file @
42eea356
...
...
@@ -5,7 +5,7 @@
{% for entry in entries %}
<div
class=
"row"
>
<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>
{% endfor %}
</div>
...
...
cps/templates/login.html
View file @
42eea356
...
...
@@ -16,7 +16,7 @@
<input
type=
"checkbox"
name=
"remember_me"
checked
>
{{_('Remember me')}}
</label>
</div>
<button
type=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
<button
type=
"submit"
name=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
</form>
</div>
{% if error %}
...
...
cps/templates/osd.xml
View file @
42eea356
...
...
@@ -2,7 +2,7 @@
<OpenSearchDescription
xmlns=
"http://a9.com/-/spec/opensearch/1.1/"
>
<LongName>
{{instance}}
</LongName>
<ShortName>
{{instance}}
</ShortName>
<Description>
{{_('
instance
Calibre Web ebook catalog')}}
</Description>
<Description>
{{_('Calibre Web ebook catalog')}}
</Description>
<Developer>
Janeczku
</Developer>
<Contact>
https://github.com/janeczku/calibre-web
</Contact>
<Url
type=
"text/html"
...
...
cps/templates/stats.html
View file @
42eea356
...
...
@@ -2,7 +2,7 @@
{% block body %}
<h3>
{{_('Linked libraries')}}
</h3>
<table
class=
"table"
>
<table
id=
"libs"
class=
"table"
>
<thead>
<tr>
<th>
{{_('Program library')}}
</th>
...
...
@@ -30,7 +30,7 @@
</table>
<h3>
{{_('Calibre library statistics')}}
</h3>
<table
class=
"table"
>
<table
id=
"stats"
class=
"table"
>
<tbody>
<tr>
<th>
{{bookcounter}}
</th>
...
...
cps/templates/user_edit.html
View file @
42eea356
...
...
@@ -27,7 +27,7 @@
<label
for=
"locale"
>
{{_('Language')}}
</label>
<select
name=
"locale"
id=
"locale"
class=
"form-control"
>
{% 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 %}
</select>
</div>
...
...
@@ -108,7 +108,7 @@
{% endif %}
<button
type=
"submit"
id=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
{% 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 %}
</form>
...
...
cps/translations/de/LC_MESSAGES/messages.po
View file @
42eea356
...
...
@@ -81,7 +81,7 @@ msgstr "Beliebte Bücher (die meisten Downloads)"
#: cps/web.py:813
msgid "Best rated books"
msgstr ""
msgstr "
Best bewertete Bücher
"
#: cps/templates/index.xml:36 cps/web.py:822
msgid "Random Books"
...
...
@@ -94,7 +94,7 @@ msgstr "Autorenliste"
#: cps/web.py:846
#, python-format
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
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
msgid "Update done"
msgstr ""
msgstr "
Update durchgeführt
"
#: cps/web.py:1128 cps/web.py:1141
msgid "search"
...
...
@@ -470,11 +470,11 @@ msgstr "Stoppe Calibre-web"
#: cps/templates/admin.html:81
msgid "Check for update"
msgstr ""
msgstr "
Suche nach Update
"
#: cps/templates/admin.html:82
msgid "Perform Update"
msgstr ""
msgstr "
Update durchführen
"
#: cps/templates/admin.html:93
msgid "Do you really want to restart Calibre-web?"
...
...
@@ -584,7 +584,7 @@ msgstr "Öffentliche Registrierung aktivieren"
#: cps/templates/config_edit.html:52
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
msgid "Admin user"
...
...
@@ -625,7 +625,7 @@ msgstr "Sprache"
#: cps/templates/detail.html:74
msgid "Publishing date"
msgstr ""
msgstr "
Herausgabedatum
"
#: cps/templates/detail.html:106
msgid "Description:"
...
...
@@ -699,11 +699,11 @@ msgstr "Beliebte Bücher"
#: cps/templates/index.xml:19
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
msgid "Best rated Books"
msgstr ""
msgstr "
Best bewertete Bücher
"
#: cps/templates/index.xml:26
msgid "Popular publications from this catalog based on Rating."
...
...
@@ -804,8 +804,8 @@ msgid "Remember me"
msgstr "Merken"
#: cps/templates/osd.xml:5
msgid "
instance
Calibre Web ebook catalog"
msgstr ""
msgid "Calibre Web ebook catalog"
msgstr "
Calibre Web Ebook Katalog
"
#: cps/templates/read.html:136
msgid "Reflow text when sidebars are open."
...
...
@@ -909,11 +909,11 @@ msgstr "Autoren in dieser Bibliothek"
#: cps/templates/stats.html:45
msgid "Categories in this Library"
msgstr ""
msgstr "
Kategorien in dieser Bibliothek
"
#: cps/templates/stats.html:49
msgid "Series in this Library"
msgstr ""
msgstr "
Serien in dieser Bibliothek
"
#: cps/templates/user_edit.html:23
msgid "Kindle E-Mail"
...
...
@@ -937,7 +937,7 @@ msgstr "Zeige Auswahl Beliebte Bücher"
#: cps/templates/user_edit.html:53
msgid "Show best rated books"
msgstr ""
msgstr "
Zeige am besten bewertete Bücher
"
#: cps/templates/user_edit.html:57
msgid "Show language selection"
...
...
cps/ub.py
View file @
42eea356
...
...
@@ -144,7 +144,6 @@ class UserBase:
else
:
return
False
def
__repr__
(
self
):
return
'<User
%
r>'
%
self
.
nickname
...
...
@@ -164,10 +163,6 @@ class User(UserBase, Base):
downloads
=
relationship
(
'Downloads'
,
backref
=
'user'
,
lazy
=
'dynamic'
)
locale
=
Column
(
String
(
2
),
default
=
"en"
)
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"
)
...
...
@@ -184,10 +179,6 @@ class Anonymous(AnonymousUserMixin, UserBase):
self
.
role
=
data
.
role
self
.
sidebar_view
=
data
.
sidebar_view
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
.
locale
=
data
.
locale
self
.
anon_browse
=
settings
.
config_anonbrowse
...
...
cps/web.py
View file @
42eea356
...
...
@@ -25,6 +25,7 @@ import zipfile
from
werkzeug.security
import
generate_password_hash
,
check_password_hash
from
babel
import
Locale
as
LC
from
babel
import
negotiate_locale
from
babel.dates
import
format_date
from
functools
import
wraps
import
base64
from
sqlalchemy.sql
import
*
...
...
@@ -280,6 +281,12 @@ def mimetype_filter(val):
s
=
'application/octet-stream'
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
):
"""
...
...
@@ -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
()
if
current_user
.
is_authenticated
:
helper
.
update_download
(
book_id
,
int
(
current_user
.
id
))
author
=
helper
.
get_normalized_author
(
book
.
author_sort
)
file_name
=
book
.
title
if
len
(
author
)
>
0
:
file_name
=
author
+
'-'
+
file_name
if
len
(
book
.
authors
)
>
0
:
file_name
=
book
.
authors
[
0
]
.
name
+
'-'
+
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
.
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
if
current_user
.
is_authenticated
:
helper
.
update_download
(
book_id
,
int
(
current_user
.
id
))
author
=
helper
.
get_normalized_author
(
book
.
author_sort
)
file_name
=
book
.
title
if
len
(
author
)
>
0
:
file_name
=
author
+
'-'
+
file_name
if
len
(
book
.
authors
)
>
0
:
file_name
=
book
.
authors
[
0
]
.
name
+
'-'
+
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
))
...
...
@@ -1240,13 +1245,7 @@ def get_download_link(book_id, format):
response
.
headers
[
"Content-Type"
]
=
mimetypes
.
types_map
[
'.'
+
format
]
except
:
pass
response
.
headers
[
"Content-Disposition"
]
=
\
"attachment; "
\
"filename={utf_filename}.{suffix};"
\
"filename*=UTF-8''{utf_filename}.{suffix}"
.
format
(
utf_filename
=
file_name
.
encode
(
'utf-8'
),
suffix
=
format
)
response
.
headers
[
"Content-Disposition"
]
=
"attachment; filename=
\"
%
s.
%
s
\"
"
%
(
file_name
.
encode
(
'utf-8'
),
format
)
return
response
else
:
abort
(
404
)
...
...
@@ -1579,9 +1578,10 @@ def profile():
@
login_required
@
admin_required
def
admin
():
commit
=
'$Format:
%
cI$'
content
=
ub
.
session
.
query
(
ub
.
User
)
.
all
()
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"
))
...
...
@@ -1669,7 +1669,7 @@ def configuration_helper(origin):
return
render_title_template
(
"config_edit.html"
,
content
=
config
,
origin
=
origin
,
title
=
_
(
u"Basic Configuration"
))
if
reboot_required
:
# db.engine.dispose() # ToDo verify correct
# db.engine.dispose() # ToDo verify correct
ub
.
session
.
close
()
ub
.
engine
.
dispose
()
# stop tornado server
...
...
@@ -1928,7 +1928,7 @@ def edit_book(book_id):
modify_database_object
(
input_authors
,
book
.
authors
,
db
.
Authors
,
db
.
session
,
'author'
)
if
author0_before_edit
!=
book
.
authors
[
0
]
.
name
:
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"
:
img
=
requests
.
get
(
to_save
[
"cover_url"
])
...
...
@@ -2168,9 +2168,10 @@ def upload():
if
is_author
:
db_author
=
is_author
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
)
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
,
datetime
.
datetime
.
now
(),
path
,
has_cover
,
db_author
,
[])
db_book
.
authors
.
append
(
db_author
)
...
...
readme.md
View file @
42eea356
...
...
@@ -11,7 +11,7 @@ Calibre Web is a web app providing a clean interface for browsing, reading and d
-
full graphical setup
-
User management
-
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
-
Filter and search by titles, authors, tags, series and language
-
Create custom book collection (shelves)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment