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
24c743d2
Commit
24c743d2
authored
Apr 19, 2020
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Code cosmetics
parent
7bb5afa5
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
262 additions
and
192 deletions
+262
-192
cli.py
cps/cli.py
+1
-1
db.py
cps/db.py
+1
-1
helper.py
cps/helper.py
+81
-55
jinjia.py
cps/jinjia.py
+5
-1
kobo.py
cps/kobo.py
+12
-5
logger.py
cps/logger.py
+1
-1
oauth.py
cps/oauth.py
+2
-2
opds.py
cps/opds.py
+43
-21
server.py
cps/server.py
+2
-2
SyncToken.py
cps/services/SyncToken.py
+7
-5
shelf.py
cps/shelf.py
+32
-27
subproc_wrapper.py
cps/subproc_wrapper.py
+5
-5
list.html
cps/templates/list.html
+6
-6
ub.py
cps/ub.py
+59
-55
updater.py
cps/updater.py
+5
-5
No files found.
cps/cli.py
View file @
24c743d2
...
@@ -34,7 +34,7 @@ def version_info():
...
@@ -34,7 +34,7 @@ def version_info():
parser
=
argparse
.
ArgumentParser
(
description
=
'Calibre Web is a web app'
parser
=
argparse
.
ArgumentParser
(
description
=
'Calibre Web is a web app'
' providing a interface for browsing, reading and downloading eBooks
\n
'
,
prog
=
'cps.py'
)
' providing a interface for browsing, reading and downloading eBooks
\n
'
,
prog
=
'cps.py'
)
parser
.
add_argument
(
'-p'
,
metavar
=
'path'
,
help
=
'path and name to settings db, e.g. /opt/cw.db'
)
parser
.
add_argument
(
'-p'
,
metavar
=
'path'
,
help
=
'path and name to settings db, e.g. /opt/cw.db'
)
parser
.
add_argument
(
'-g'
,
metavar
=
'path'
,
help
=
'path and name to gdrive db, e.g. /opt/gd.db'
)
parser
.
add_argument
(
'-g'
,
metavar
=
'path'
,
help
=
'path and name to gdrive db, e.g. /opt/gd.db'
)
parser
.
add_argument
(
'-c'
,
metavar
=
'path'
,
parser
.
add_argument
(
'-c'
,
metavar
=
'path'
,
...
...
cps/db.py
View file @
24c743d2
...
@@ -25,7 +25,7 @@ import ast
...
@@ -25,7 +25,7 @@ import ast
from
sqlalchemy
import
create_engine
from
sqlalchemy
import
create_engine
from
sqlalchemy
import
Table
,
Column
,
ForeignKey
from
sqlalchemy
import
Table
,
Column
,
ForeignKey
from
sqlalchemy
import
String
,
Integer
,
Boolean
,
TIMESTAMP
,
Float
from
sqlalchemy
import
String
,
Integer
,
Boolean
,
TIMESTAMP
,
Float
,
DateTime
from
sqlalchemy.orm
import
relationship
,
sessionmaker
,
scoped_session
from
sqlalchemy.orm
import
relationship
,
sessionmaker
,
scoped_session
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
...
...
cps/helper.py
View file @
24c743d2
...
@@ -96,7 +96,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
...
@@ -96,7 +96,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
# read settings and append converter task to queue
# read settings and append converter task to queue
if
kindle_mail
:
if
kindle_mail
:
settings
=
config
.
get_mail_settings
()
settings
=
config
.
get_mail_settings
()
settings
[
'subject'
]
=
_
(
'Send to Kindle'
)
# pretranslate Subject for e-mail
settings
[
'subject'
]
=
_
(
'Send to Kindle'
)
# pretranslate Subject for e-mail
settings
[
'body'
]
=
_
(
u'This e-mail has been sent via Calibre-Web.'
)
settings
[
'body'
]
=
_
(
u'This e-mail has been sent via Calibre-Web.'
)
# text = _(u"%(format)s: %(book)s", format=new_book_format, book=book.title)
# text = _(u"%(format)s: %(book)s", format=new_book_format, book=book.title)
else
:
else
:
...
@@ -108,7 +108,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
...
@@ -108,7 +108,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
return
None
return
None
else
:
else
:
error_message
=
_
(
u"
%(format)
s not found:
%(fn)
s"
,
error_message
=
_
(
u"
%(format)
s not found:
%(fn)
s"
,
format
=
old_book_format
,
fn
=
data
.
name
+
"."
+
old_book_format
.
lower
())
format
=
old_book_format
,
fn
=
data
.
name
+
"."
+
old_book_format
.
lower
())
return
error_message
return
error_message
...
@@ -141,36 +141,52 @@ def check_send_to_kindle(entry):
...
@@ -141,36 +141,52 @@ def check_send_to_kindle(entry):
returns all available book formats for sending to Kindle
returns all available book formats for sending to Kindle
"""
"""
if
len
(
entry
.
data
):
if
len
(
entry
.
data
):
bookformats
=
list
()
bookformats
=
list
()
if
config
.
config_ebookconverter
==
0
:
if
config
.
config_ebookconverter
==
0
:
# no converter - only for mobi and pdf formats
# no converter - only for mobi and pdf formats
for
ele
in
iter
(
entry
.
data
):
for
ele
in
iter
(
entry
.
data
):
if
'MOBI'
in
ele
.
format
:
if
'MOBI'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'PDF'
in
ele
.
format
:
if
'PDF'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
'AZW'
in
ele
.
format
:
if
'AZW'
in
ele
.
format
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
bookformats
.
append
({
'format'
:
'Azw'
,
'''if 'AZW3' in ele.format:
'convert'
:
0
,
bookformats.append({'format':'Azw3','convert':0,'text':_('Send
%(format)
s to Kindle',format='Azw3')})'''
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
else
:
else
:
formats
=
list
()
formats
=
list
()
for
ele
in
iter
(
entry
.
data
):
for
ele
in
iter
(
entry
.
data
):
formats
.
append
(
ele
.
format
)
formats
.
append
(
ele
.
format
)
if
'MOBI'
in
formats
:
if
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Mobi'
)})
if
'AZW'
in
formats
:
if
'AZW'
in
formats
:
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
bookformats
.
append
({
'format'
:
'Azw'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw'
)})
if
'PDF'
in
formats
:
if
'PDF'
in
formats
:
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
bookformats
.
append
({
'format'
:
'Pdf'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Pdf'
)})
if
config
.
config_ebookconverter
>=
1
:
if
config
.
config_ebookconverter
>=
1
:
if
'EPUB'
in
formats
and
not
'MOBI'
in
formats
:
if
'EPUB'
in
formats
and
not
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
1
,
bookformats
.
append
({
'format'
:
'Mobi'
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
'convert'
:
1
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
if
config
.
config_ebookconverter
==
2
:
if
config
.
config_ebookconverter
==
2
:
if
'AZW3'
in
formats
and
not
'MOBI'
in
formats
:
if
'AZW3'
in
formats
and
not
'MOBI'
in
formats
:
bookformats
.
append
({
'format'
:
'Mobi'
,
'convert'
:
2
,
bookformats
.
append
({
'format'
:
'Mobi'
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Azw3'
,
format
=
'Mobi'
)})
'convert'
:
2
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Azw3'
,
format
=
'Mobi'
)})
return
bookformats
return
bookformats
else
:
else
:
log
.
error
(
u'Cannot find book entry
%
d'
,
entry
.
id
)
log
.
error
(
u'Cannot find book entry
%
d'
,
entry
.
id
)
...
@@ -204,7 +220,6 @@ def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id):
...
@@ -204,7 +220,6 @@ def send_mail(book_id, book_format, convert, kindle_mail, calibrepath, user_id):
# returns None if success, otherwise errormessage
# returns None if success, otherwise errormessage
return
convert_book_format
(
book_id
,
calibrepath
,
u'azw3'
,
book_format
.
lower
(),
user_id
,
kindle_mail
)
return
convert_book_format
(
book_id
,
calibrepath
,
u'azw3'
,
book_format
.
lower
(),
user_id
,
kindle_mail
)
for
entry
in
iter
(
book
.
data
):
for
entry
in
iter
(
book
.
data
):
if
entry
.
format
.
upper
()
==
book_format
.
upper
():
if
entry
.
format
.
upper
()
==
book_format
.
upper
():
converted_file_name
=
entry
.
name
+
'.'
+
book_format
.
lower
()
converted_file_name
=
entry
.
name
+
'.'
+
book_format
.
lower
()
...
@@ -369,7 +384,7 @@ def update_dir_structure_gdrive(book_id, first_author):
...
@@ -369,7 +384,7 @@ def update_dir_structure_gdrive(book_id, first_author):
path
=
book
.
path
path
=
book
.
path
gd
.
updateDatabaseOnEdit
(
gFile
[
'id'
],
book
.
path
)
# only child folder affected
gd
.
updateDatabaseOnEdit
(
gFile
[
'id'
],
book
.
path
)
# only child folder affected
else
:
else
:
error
=
_
(
u'File
%(file)
s not found on Google Drive'
,
file
=
book
.
path
)
# file not found
error
=
_
(
u'File
%(file)
s not found on Google Drive'
,
file
=
book
.
path
)
# file not found
if
authordir
!=
new_authordir
:
if
authordir
!=
new_authordir
:
gFile
=
gd
.
getFileFromEbooksFolder
(
os
.
path
.
dirname
(
book
.
path
),
new_titledir
)
gFile
=
gd
.
getFileFromEbooksFolder
(
os
.
path
.
dirname
(
book
.
path
),
new_titledir
)
...
@@ -379,7 +394,7 @@ def update_dir_structure_gdrive(book_id, first_author):
...
@@ -379,7 +394,7 @@ def update_dir_structure_gdrive(book_id, first_author):
path
=
book
.
path
path
=
book
.
path
gd
.
updateDatabaseOnEdit
(
gFile
[
'id'
],
book
.
path
)
gd
.
updateDatabaseOnEdit
(
gFile
[
'id'
],
book
.
path
)
else
:
else
:
error
=
_
(
u'File
%(file)
s not found on Google Drive'
,
file
=
authordir
)
# file not found
error
=
_
(
u'File
%(file)
s not found on Google Drive'
,
file
=
authordir
)
# file not found
# Rename all files from old names to new names
# Rename all files from old names to new names
if
authordir
!=
new_authordir
or
titledir
!=
new_titledir
:
if
authordir
!=
new_authordir
or
titledir
!=
new_titledir
:
...
@@ -395,7 +410,7 @@ def update_dir_structure_gdrive(book_id, first_author):
...
@@ -395,7 +410,7 @@ def update_dir_structure_gdrive(book_id, first_author):
def
delete_book_gdrive
(
book
,
book_format
):
def
delete_book_gdrive
(
book
,
book_format
):
error
=
False
error
=
False
if
book_format
:
if
book_format
:
name
=
''
name
=
''
for
entry
in
book
.
data
:
for
entry
in
book
.
data
:
...
@@ -403,12 +418,12 @@ def delete_book_gdrive(book, book_format):
...
@@ -403,12 +418,12 @@ def delete_book_gdrive(book, book_format):
name
=
entry
.
name
+
'.'
+
book_format
name
=
entry
.
name
+
'.'
+
book_format
gFile
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
name
)
gFile
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
name
)
else
:
else
:
gFile
=
gd
.
getFileFromEbooksFolder
(
os
.
path
.
dirname
(
book
.
path
),
book
.
path
.
split
(
'/'
)[
1
])
gFile
=
gd
.
getFileFromEbooksFolder
(
os
.
path
.
dirname
(
book
.
path
),
book
.
path
.
split
(
'/'
)[
1
])
if
gFile
:
if
gFile
:
gd
.
deleteDatabaseEntry
(
gFile
[
'id'
])
gd
.
deleteDatabaseEntry
(
gFile
[
'id'
])
gFile
.
Trash
()
gFile
.
Trash
()
else
:
else
:
error
=
_
(
u'Book path
%(path)
s not found on Google Drive'
,
path
=
book
.
path
)
# file not found
error
=
_
(
u'Book path
%(path)
s not found on Google Drive'
,
path
=
book
.
path
)
# file not found
return
error
return
error
...
@@ -417,24 +432,25 @@ def reset_password(user_id):
...
@@ -417,24 +432,25 @@ def reset_password(user_id):
password
=
generate_random_password
()
password
=
generate_random_password
()
existing_user
.
password
=
generate_password_hash
(
password
)
existing_user
.
password
=
generate_password_hash
(
password
)
if
not
config
.
get_mail_server_configured
():
if
not
config
.
get_mail_server_configured
():
return
(
2
,
None
)
return
2
,
None
try
:
try
:
ub
.
session
.
commit
()
ub
.
session
.
commit
()
send_registration_mail
(
existing_user
.
email
,
existing_user
.
nickname
,
password
,
True
)
send_registration_mail
(
existing_user
.
email
,
existing_user
.
nickname
,
password
,
True
)
return
(
1
,
existing_user
.
nickname
)
return
1
,
existing_user
.
nickname
except
Exception
:
except
Exception
:
ub
.
session
.
rollback
()
ub
.
session
.
rollback
()
return
(
0
,
None
)
return
0
,
None
def
generate_random_password
():
def
generate_random_password
():
s
=
"abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$
%
&*()?"
s
=
"abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$
%
&*()?"
passlen
=
8
passlen
=
8
return
""
.
join
(
random
.
sample
(
s
,
passlen
))
return
""
.
join
(
random
.
sample
(
s
,
passlen
))
################################## External interface
################################## External interface
def
update_dir_stucture
(
book_id
,
calibrepath
,
first_author
=
None
):
def
update_dir_stucture
(
book_id
,
calibrepath
,
first_author
=
None
):
if
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
return
update_dir_structure_gdrive
(
book_id
,
first_author
)
return
update_dir_structure_gdrive
(
book_id
,
first_author
)
else
:
else
:
...
@@ -454,23 +470,26 @@ def get_cover_on_failure(use_generic_cover):
...
@@ -454,23 +470,26 @@ def get_cover_on_failure(use_generic_cover):
else
:
else
:
return
None
return
None
def
get_book_cover
(
book_id
):
def
get_book_cover
(
book_id
):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
common_filters
())
.
first
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
common_filters
())
.
first
()
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
=
True
)
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
=
True
)
def
get_book_cover_with_uuid
(
book_uuid
,
def
get_book_cover_with_uuid
(
book_uuid
,
use_generic_cover_on_failure
=
True
):
use_generic_cover_on_failure
=
True
):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
==
book_uuid
)
.
first
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
==
book_uuid
)
.
first
()
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
)
return
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
)
def
get_book_cover_internal
(
book
,
def
get_book_cover_internal
(
book
,
use_generic_cover_on_failure
):
use_generic_cover_on_failure
):
if
book
and
book
.
has_cover
:
if
book
and
book
.
has_cover
:
if
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
try
:
try
:
if
not
gd
.
is_gdrive_ready
():
if
not
gd
.
is_gdrive_ready
():
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
return
get_cover_on_failure
(
use_generic_cover_on_failure
)
path
=
gd
.
get_cover_via_gdrive
(
book
.
path
)
path
=
gd
.
get_cover_via_gdrive
(
book
.
path
)
if
path
:
if
path
:
return
redirect
(
path
)
return
redirect
(
path
)
else
:
else
:
...
@@ -530,7 +549,7 @@ def save_cover(img, book_path):
...
@@ -530,7 +549,7 @@ def save_cover(img, book_path):
return
False
,
_
(
"Only jpg/jpeg/png/webp files are supported as coverfile"
)
return
False
,
_
(
"Only jpg/jpeg/png/webp files are supported as coverfile"
)
# convert to jpg because calibre only supports jpg
# convert to jpg because calibre only supports jpg
if
content_type
in
(
'image/png'
,
'image/webp'
):
if
content_type
in
(
'image/png'
,
'image/webp'
):
if
hasattr
(
img
,
'stream'
):
if
hasattr
(
img
,
'stream'
):
imgc
=
PILImage
.
open
(
img
.
stream
)
imgc
=
PILImage
.
open
(
img
.
stream
)
else
:
else
:
imgc
=
PILImage
.
open
(
io
.
BytesIO
(
img
.
content
))
imgc
=
PILImage
.
open
(
io
.
BytesIO
(
img
.
content
))
...
@@ -539,7 +558,7 @@ def save_cover(img, book_path):
...
@@ -539,7 +558,7 @@ def save_cover(img, book_path):
im
.
save
(
tmp_bytesio
,
format
=
'JPEG'
)
im
.
save
(
tmp_bytesio
,
format
=
'JPEG'
)
img
.
_content
=
tmp_bytesio
.
getvalue
()
img
.
_content
=
tmp_bytesio
.
getvalue
()
else
:
else
:
if
content_type
not
in
(
'image/jpeg'
)
:
if
content_type
not
in
'image/jpeg'
:
log
.
error
(
"Only jpg/jpeg files are supported as coverfile"
)
log
.
error
(
"Only jpg/jpeg files are supported as coverfile"
)
return
False
,
_
(
"Only jpg/jpeg files are supported as coverfile"
)
return
False
,
_
(
"Only jpg/jpeg files are supported as coverfile"
)
...
@@ -557,7 +576,6 @@ def save_cover(img, book_path):
...
@@ -557,7 +576,6 @@ def save_cover(img, book_path):
return
save_cover_from_filestorage
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book_path
),
"cover.jpg"
,
img
)
return
save_cover_from_filestorage
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book_path
),
"cover.jpg"
,
img
)
def
do_download_file
(
book
,
book_format
,
data
,
headers
):
def
do_download_file
(
book
,
book_format
,
data
,
headers
):
if
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
startTime
=
time
.
time
()
startTime
=
time
.
time
()
...
@@ -579,7 +597,6 @@ def do_download_file(book, book_format, data, headers):
...
@@ -579,7 +597,6 @@ def do_download_file(book, book_format, data, headers):
##################################
##################################
def
check_unrar
(
unrarLocation
):
def
check_unrar
(
unrarLocation
):
if
not
unrarLocation
:
if
not
unrarLocation
:
return
return
...
@@ -601,13 +618,12 @@ def check_unrar(unrarLocation):
...
@@ -601,13 +618,12 @@ def check_unrar(unrarLocation):
return
'Error excecuting UnRar'
return
'Error excecuting UnRar'
def
json_serial
(
obj
):
def
json_serial
(
obj
):
"""JSON serializer for objects not serializable by default json code"""
"""JSON serializer for objects not serializable by default json code"""
if
isinstance
(
obj
,
(
datetime
)
):
if
isinstance
(
obj
,
datetime
):
return
obj
.
isoformat
()
return
obj
.
isoformat
()
if
isinstance
(
obj
,
(
timedelta
)
):
if
isinstance
(
obj
,
timedelta
):
return
{
return
{
'__type__'
:
'timedelta'
,
'__type__'
:
'timedelta'
,
'days'
:
obj
.
days
,
'days'
:
obj
.
days
,
...
@@ -615,7 +631,7 @@ def json_serial(obj):
...
@@ -615,7 +631,7 @@ def json_serial(obj):
'microseconds'
:
obj
.
microseconds
,
'microseconds'
:
obj
.
microseconds
,
}
}
# return obj.isoformat()
# return obj.isoformat()
raise
TypeError
(
"Type
%
s not serializable"
%
type
(
obj
))
raise
TypeError
(
"Type
%
s not serializable"
%
type
(
obj
))
# helper function for displaying the runtime of tasks
# helper function for displaying the runtime of tasks
...
@@ -637,7 +653,7 @@ def format_runtime(runtime):
...
@@ -637,7 +653,7 @@ def format_runtime(runtime):
# helper function to apply localize status information in tasklist entries
# helper function to apply localize status information in tasklist entries
def
render_task_status
(
tasklist
):
def
render_task_status
(
tasklist
):
renderedtasklist
=
list
()
renderedtasklist
=
list
()
for
task
in
tasklist
:
for
task
in
tasklist
:
if
task
[
'user'
]
==
current_user
.
nickname
or
current_user
.
role_admin
():
if
task
[
'user'
]
==
current_user
.
nickname
or
current_user
.
role_admin
():
if
task
[
'formStarttime'
]:
if
task
[
'formStarttime'
]:
...
@@ -653,7 +669,7 @@ def render_task_status(tasklist):
...
@@ -653,7 +669,7 @@ def render_task_status(tasklist):
task
[
'runtime'
]
=
format_runtime
(
task
[
'formRuntime'
])
task
[
'runtime'
]
=
format_runtime
(
task
[
'formRuntime'
])
# localize the task status
# localize the task status
if
isinstance
(
task
[
'stat'
],
int
):
if
isinstance
(
task
[
'stat'
],
int
):
if
task
[
'stat'
]
==
STAT_WAITING
:
if
task
[
'stat'
]
==
STAT_WAITING
:
task
[
'status'
]
=
_
(
u'Waiting'
)
task
[
'status'
]
=
_
(
u'Waiting'
)
elif
task
[
'stat'
]
==
STAT_FAIL
:
elif
task
[
'stat'
]
==
STAT_FAIL
:
...
@@ -666,14 +682,14 @@ def render_task_status(tasklist):
...
@@ -666,14 +682,14 @@ def render_task_status(tasklist):
task
[
'status'
]
=
_
(
u'Unknown Status'
)
task
[
'status'
]
=
_
(
u'Unknown Status'
)
# localize the task type
# localize the task type
if
isinstance
(
task
[
'taskType'
],
int
):
if
isinstance
(
task
[
'taskType'
],
int
):
if
task
[
'taskType'
]
==
TASK_EMAIL
:
if
task
[
'taskType'
]
==
TASK_EMAIL
:
task
[
'taskMessage'
]
=
_
(
u'E-mail: '
)
+
task
[
'taskMess'
]
task
[
'taskMessage'
]
=
_
(
u'E-mail: '
)
+
task
[
'taskMess'
]
elif
task
[
'taskType'
]
==
TASK_CONVERT
:
elif
task
[
'taskType'
]
==
TASK_CONVERT
:
task
[
'taskMessage'
]
=
_
(
u'Convert: '
)
+
task
[
'taskMess'
]
task
[
'taskMessage'
]
=
_
(
u'Convert: '
)
+
task
[
'taskMess'
]
elif
task
[
'taskType'
]
==
TASK_UPLOAD
:
elif
task
[
'taskType'
]
==
TASK_UPLOAD
:
task
[
'taskMessage'
]
=
_
(
u'Upload: '
)
+
task
[
'taskMess'
]
task
[
'taskMessage'
]
=
_
(
u'Upload: '
)
+
task
[
'taskMess'
]
elif
task
[
'taskType'
]
==
TASK_CONVERT_ANY
:
elif
task
[
'taskType'
]
==
TASK_CONVERT_ANY
:
task
[
'taskMessage'
]
=
_
(
u'Convert: '
)
+
task
[
'taskMess'
]
task
[
'taskMessage'
]
=
_
(
u'Convert: '
)
+
task
[
'taskMess'
]
else
:
else
:
task
[
'taskMessage'
]
=
_
(
u'Unknown Task: '
)
+
task
[
'taskMess'
]
task
[
'taskMessage'
]
=
_
(
u'Unknown Task: '
)
+
task
[
'taskMess'
]
...
@@ -709,11 +725,11 @@ def common_filters(allow_show_archived=False):
...
@@ -709,11 +725,11 @@ def common_filters(allow_show_archived=False):
pos_cc_list
=
current_user
.
allowed_column_value
.
split
(
','
)
pos_cc_list
=
current_user
.
allowed_column_value
.
split
(
','
)
pos_content_cc_filter
=
true
()
if
pos_cc_list
==
[
''
]
else
\
pos_content_cc_filter
=
true
()
if
pos_cc_list
==
[
''
]
else
\
getattr
(
db
.
Books
,
'custom_column_'
+
str
(
config
.
config_restricted_column
))
.
\
getattr
(
db
.
Books
,
'custom_column_'
+
str
(
config
.
config_restricted_column
))
.
\
any
(
db
.
cc_classes
[
config
.
config_restricted_column
]
.
value
.
in_
(
pos_cc_list
))
any
(
db
.
cc_classes
[
config
.
config_restricted_column
]
.
value
.
in_
(
pos_cc_list
))
neg_cc_list
=
current_user
.
denied_column_value
.
split
(
','
)
neg_cc_list
=
current_user
.
denied_column_value
.
split
(
','
)
neg_content_cc_filter
=
false
()
if
neg_cc_list
==
[
''
]
else
\
neg_content_cc_filter
=
false
()
if
neg_cc_list
==
[
''
]
else
\
getattr
(
db
.
Books
,
'custom_column_'
+
str
(
config
.
config_restricted_column
))
.
\
getattr
(
db
.
Books
,
'custom_column_'
+
str
(
config
.
config_restricted_column
))
.
\
any
(
db
.
cc_classes
[
config
.
config_restricted_column
]
.
value
.
in_
(
neg_cc_list
))
any
(
db
.
cc_classes
[
config
.
config_restricted_column
]
.
value
.
in_
(
neg_cc_list
))
else
:
else
:
pos_content_cc_filter
=
true
()
pos_content_cc_filter
=
true
()
neg_content_cc_filter
=
false
()
neg_content_cc_filter
=
false
()
...
@@ -733,8 +749,9 @@ def tags_filters():
...
@@ -733,8 +749,9 @@ def tags_filters():
# Creates for all stored languages a translated speaking name in the array for the UI
# Creates for all stored languages a translated speaking name in the array for the UI
def
speaking_language
(
languages
=
None
):
def
speaking_language
(
languages
=
None
):
if
not
languages
:
if
not
languages
:
languages
=
db
.
session
.
query
(
db
.
Languages
)
.
join
(
db
.
books_languages_link
)
.
join
(
db
.
Books
)
.
filter
(
common_filters
())
\
languages
=
db
.
session
.
query
(
db
.
Languages
)
.
join
(
db
.
books_languages_link
)
.
join
(
db
.
Books
)
\
.
group_by
(
text
(
'books_languages_link.lang_code'
))
.
all
()
.
filter
(
common_filters
())
\
.
group_by
(
text
(
'books_languages_link.lang_code'
))
.
all
()
for
lang
in
languages
:
for
lang
in
languages
:
try
:
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
...
@@ -743,6 +760,7 @@ def speaking_language(languages=None):
...
@@ -743,6 +760,7 @@ def speaking_language(languages=None):
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
return
languages
return
languages
# checks if domain is in database (including wildcards)
# checks if domain is in database (including wildcards)
# example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name;
# example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name;
# from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/
# from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/
...
@@ -787,21 +805,25 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s
...
@@ -787,21 +805,25 @@ def fill_indexpage_with_archived_books(page, database, db_filter, order, allow_s
randm
=
false
()
randm
=
false
()
off
=
int
(
int
(
config
.
config_books_per_page
)
*
(
page
-
1
))
off
=
int
(
int
(
config
.
config_books_per_page
)
*
(
page
-
1
))
pagination
=
Pagination
(
page
,
config
.
config_books_per_page
,
pagination
=
Pagination
(
page
,
config
.
config_books_per_page
,
len
(
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
.
filter
(
common_filters
(
allow_show_archived
))
.
all
()))
len
(
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
entries
=
db
.
session
.
query
(
database
)
.
join
(
*
join
,
isouter
=
True
)
.
filter
(
db_filter
)
.
filter
(
common_filters
(
allow_show_archived
))
.
\
.
filter
(
common_filters
(
allow_show_archived
))
.
all
()))
order_by
(
*
order
)
.
offset
(
off
)
.
limit
(
config
.
config_books_per_page
)
.
all
()
entries
=
db
.
session
.
query
(
database
)
.
join
(
*
join
,
isouter
=
True
)
.
filter
(
db_filter
)
\
.
filter
(
common_filters
(
allow_show_archived
))
\
.
order_by
(
*
order
)
.
offset
(
off
)
.
limit
(
config
.
config_books_per_page
)
.
all
()
for
book
in
entries
:
for
book
in
entries
:
book
=
order_authors
(
book
)
book
=
order_authors
(
book
)
return
entries
,
randm
,
pagination
return
entries
,
randm
,
pagination
def
get_typeahead
(
database
,
query
,
replace
=
(
''
,
''
),
tag_filter
=
true
()):
def
get_typeahead
(
database
,
query
,
replace
=
(
''
,
''
),
tag_filter
=
true
()):
query
=
query
or
''
query
=
query
or
''
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
entries
=
db
.
session
.
query
(
database
)
.
filter
(
tag_filter
)
.
filter
(
func
.
lower
(
database
.
name
)
.
ilike
(
"
%
"
+
query
+
"
%
"
))
.
all
()
entries
=
db
.
session
.
query
(
database
)
.
filter
(
tag_filter
)
.
\
filter
(
func
.
lower
(
database
.
name
)
.
ilike
(
"
%
"
+
query
+
"
%
"
))
.
all
()
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
.
replace
(
*
replace
))
for
r
in
entries
])
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
.
replace
(
*
replace
))
for
r
in
entries
])
return
json_dumps
return
json_dumps
# read search results from calibre-database and return it (function is used for feed and simple search
# read search results from calibre-database and return it (function is used for feed and simple search
def
get_search_results
(
term
):
def
get_search_results
(
term
):
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
...
@@ -820,6 +842,7 @@ def get_search_results(term):
...
@@ -820,6 +842,7 @@ def get_search_results(term):
func
.
lower
(
db
.
Books
.
title
)
.
ilike
(
"
%
"
+
term
+
"
%
"
)
func
.
lower
(
db
.
Books
.
title
)
.
ilike
(
"
%
"
+
term
+
"
%
"
)
))
.
all
()
))
.
all
()
def
get_cc_columns
():
def
get_cc_columns
():
tmpcc
=
db
.
session
.
query
(
db
.
Custom_Columns
)
.
filter
(
db
.
Custom_Columns
.
datatype
.
notin_
(
db
.
cc_exceptions
))
.
all
()
tmpcc
=
db
.
session
.
query
(
db
.
Custom_Columns
)
.
filter
(
db
.
Custom_Columns
.
datatype
.
notin_
(
db
.
cc_exceptions
))
.
all
()
if
config
.
config_columns_to_ignore
:
if
config
.
config_columns_to_ignore
:
...
@@ -832,6 +855,7 @@ def get_cc_columns():
...
@@ -832,6 +855,7 @@ def get_cc_columns():
cc
=
tmpcc
cc
=
tmpcc
return
cc
return
cc
def
get_download_link
(
book_id
,
book_format
):
def
get_download_link
(
book_id
,
book_format
):
book_format
=
book_format
.
split
(
"."
)[
0
]
book_format
=
book_format
.
split
(
"."
)[
0
]
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
common_filters
())
.
first
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
common_filters
())
.
first
()
...
@@ -856,7 +880,8 @@ def get_download_link(book_id, book_format):
...
@@ -856,7 +880,8 @@ def get_download_link(book_id, book_format):
else
:
else
:
abort
(
404
)
abort
(
404
)
def
check_exists_book
(
authr
,
title
):
def
check_exists_book
(
authr
,
title
):
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
lcase
)
q
=
list
()
q
=
list
()
authorterms
=
re
.
split
(
r'\s*&\s*'
,
authr
)
authorterms
=
re
.
split
(
r'\s*&\s*'
,
authr
)
...
@@ -865,11 +890,12 @@ def check_exists_book(authr,title):
...
@@ -865,11 +890,12 @@ def check_exists_book(authr,title):
return
db
.
session
.
query
(
db
.
Books
)
.
filter
(
return
db
.
session
.
query
(
db
.
Books
)
.
filter
(
and_
(
db
.
Books
.
authors
.
any
(
and_
(
*
q
)),
and_
(
db
.
Books
.
authors
.
any
(
and_
(
*
q
)),
func
.
lower
(
db
.
Books
.
title
)
.
ilike
(
"
%
"
+
title
+
"
%
"
)
func
.
lower
(
db
.
Books
.
title
)
.
ilike
(
"
%
"
+
title
+
"
%
"
)
))
.
first
()
))
.
first
()
############### Database Helper functions
############### Database Helper functions
def
lcase
(
s
):
def
lcase
(
s
):
try
:
try
:
return
unidecode
.
unidecode
(
s
.
lower
())
return
unidecode
.
unidecode
(
s
.
lower
())
...
...
cps/jinjia.py
View file @
24c743d2
...
@@ -80,9 +80,13 @@ def formatdate_filter(val):
...
@@ -80,9 +80,13 @@ def formatdate_filter(val):
formatdate
=
datetime
.
datetime
.
strptime
(
conformed_timestamp
[:
15
],
"
%
Y
%
m
%
d
%
H
%
M
%
S"
)
formatdate
=
datetime
.
datetime
.
strptime
(
conformed_timestamp
[:
15
],
"
%
Y
%
m
%
d
%
H
%
M
%
S"
)
return
format_date
(
formatdate
,
format
=
'medium'
,
locale
=
get_locale
())
return
format_date
(
formatdate
,
format
=
'medium'
,
locale
=
get_locale
())
except
AttributeError
as
e
:
except
AttributeError
as
e
:
log
.
error
(
'Babel error:
%
s, Current user locale:
%
s, Current User:
%
s'
,
e
,
current_user
.
locale
,
current_user
.
nickname
)
log
.
error
(
'Babel error:
%
s, Current user locale:
%
s, Current User:
%
s'
,
e
,
current_user
.
locale
,
current_user
.
nickname
)
return
formatdate
return
formatdate
@
jinjia
.
app_template_filter
(
'formatdateinput'
)
@
jinjia
.
app_template_filter
(
'formatdateinput'
)
def
format_date_input
(
val
):
def
format_date_input
(
val
):
conformed_timestamp
=
re
.
sub
(
r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))"
,
''
,
val
)
conformed_timestamp
=
re
.
sub
(
r"[:]|([-](?!((\d{2}[:]\d{2})|(\d{4}))$))"
,
''
,
val
)
...
...
cps/kobo.py
View file @
24c743d2
...
@@ -385,7 +385,7 @@ def get_metadata(book):
...
@@ -385,7 +385,7 @@ def get_metadata(book):
name
=
get_series
(
book
)
name
=
get_series
(
book
)
metadata
[
"Series"
]
=
{
metadata
[
"Series"
]
=
{
"Name"
:
get_series
(
book
),
"Name"
:
get_series
(
book
),
"Number"
:
book
.
series_index
,
"Number"
:
book
.
series_index
,
# ToDo Check int() ?
"NumberFloat"
:
float
(
book
.
series_index
),
"NumberFloat"
:
float
(
book
.
series_index
),
# Get a deterministic id based on the series name.
# Get a deterministic id based on the series name.
"Id"
:
uuid
.
uuid3
(
uuid
.
NAMESPACE_DNS
,
name
),
"Id"
:
uuid
.
uuid3
(
uuid
.
NAMESPACE_DNS
,
name
),
...
@@ -407,8 +407,10 @@ def HandleTagCreate():
...
@@ -407,8 +407,10 @@ def HandleTagCreate():
log
.
debug
(
"Received malformed v1/library/tags request."
)
log
.
debug
(
"Received malformed v1/library/tags request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Name' or 'Items' field"
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Name' or 'Items' field"
)
# ToDO: Names are not unique ! -> filter only private shelfs
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
and_
(
ub
.
Shelf
.
name
)
==
name
,
ub
.
Shelf
.
user_id
==
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
and_
(
ub
.
Shelf
.
name
)
==
name
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
current_user
.
id
)
.
one_or_none
()
# ToDO: shouldn't it ) at the end
if
shelf
and
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
if
shelf
and
not
shelf_lib
.
check_shelf_edit_permissions
(
shelf
):
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
abort
(
401
,
description
=
"User is unauthaurized to edit shelf."
)
...
@@ -517,6 +519,7 @@ def HandleTagRemoveItem(tag_id):
...
@@ -517,6 +519,7 @@ def HandleTagRemoveItem(tag_id):
log
.
debug
(
"Received malformed v1/library/tags/<tag_id>/items/delete request."
)
log
.
debug
(
"Received malformed v1/library/tags/<tag_id>/items/delete request."
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Items' field"
)
abort
(
400
,
description
=
"Malformed tags POST request. Data is missing 'Items' field"
)
# insconsitent to above requests
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
uuid
==
tag_id
,
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
uuid
==
tag_id
,
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
ub
.
Shelf
.
user_id
==
current_user
.
id
)
.
one_or_none
()
if
not
shelf
:
if
not
shelf
:
...
@@ -552,7 +555,8 @@ def HandleTagRemoveItem(tag_id):
...
@@ -552,7 +555,8 @@ def HandleTagRemoveItem(tag_id):
def
sync_shelves
(
sync_token
,
sync_results
):
def
sync_shelves
(
sync_token
,
sync_results
):
new_tags_last_modified
=
sync_token
.
tags_last_modified
new_tags_last_modified
=
sync_token
.
tags_last_modified
for
shelf
in
ub
.
session
.
query
(
ub
.
ShelfArchive
)
.
filter
(
func
.
datetime
(
ub
.
ShelfArchive
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
ShelfArchive
.
user_id
==
current_user
.
id
):
for
shelf
in
ub
.
session
.
query
(
ub
.
ShelfArchive
)
.
filter
(
func
.
datetime
(
ub
.
ShelfArchive
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
ShelfArchive
.
user_id
==
current_user
.
id
):
new_tags_last_modified
=
max
(
shelf
.
last_modified
,
new_tags_last_modified
)
new_tags_last_modified
=
max
(
shelf
.
last_modified
,
new_tags_last_modified
)
sync_results
.
append
({
sync_results
.
append
({
...
@@ -564,7 +568,8 @@ def sync_shelves(sync_token, sync_results):
...
@@ -564,7 +568,8 @@ def sync_shelves(sync_token, sync_results):
}
}
})
})
for
shelf
in
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
func
.
datetime
(
ub
.
Shelf
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
Shelf
.
user_id
==
current_user
.
id
):
for
shelf
in
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
func
.
datetime
(
ub
.
Shelf
.
last_modified
)
>
sync_token
.
tags_last_modified
,
ub
.
Shelf
.
user_id
==
current_user
.
id
):
if
not
shelf_lib
.
check_shelf_view_permissions
(
shelf
):
if
not
shelf_lib
.
check_shelf_view_permissions
(
shelf
):
continue
continue
...
@@ -600,6 +605,7 @@ def create_kobo_tag(shelf):
...
@@ -600,6 +605,7 @@ def create_kobo_tag(shelf):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_shelf
.
book_id
)
.
one_or_none
()
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_shelf
.
book_id
)
.
one_or_none
()
if
not
book
:
if
not
book
:
log
.
info
(
u"Book (id:
%
s) in BookShelf (id:
%
s) not found in book database"
,
book_shelf
.
book_id
,
shelf
.
id
)
log
.
info
(
u"Book (id:
%
s) in BookShelf (id:
%
s) not found in book database"
,
book_shelf
.
book_id
,
shelf
.
id
)
# ToDo shouldn't it continue?
return
None
return
None
tag
[
"Items"
]
.
append
(
tag
[
"Items"
]
.
append
(
{
{
...
@@ -769,7 +775,8 @@ def HandleCoverImageRequest(book_uuid, width, height,Quality, isGreyscale):
...
@@ -769,7 +775,8 @@ def HandleCoverImageRequest(book_uuid, width, height,Quality, isGreyscale):
height
=
height
),
307
)
height
=
height
),
307
)
else
:
else
:
log
.
debug
(
"Cover for unknown book:
%
s requested"
%
book_uuid
)
log
.
debug
(
"Cover for unknown book:
%
s requested"
%
book_uuid
)
return
redirect_or_proxy_request
()
# additional proxy request make no sense, -> direct return
return
make_response
(
jsonify
({}))
log
.
debug
(
"Cover request received for book
%
s"
%
book_uuid
)
log
.
debug
(
"Cover request received for book
%
s"
%
book_uuid
)
return
book_cover
return
book_cover
...
...
cps/logger.py
View file @
24c743d2
...
@@ -108,7 +108,7 @@ def setup(log_file, log_level=None):
...
@@ -108,7 +108,7 @@ def setup(log_file, log_level=None):
r
.
setLevel
(
log_level
)
r
.
setLevel
(
log_level
)
# Otherwise name get's destroyed on windows
# Otherwise name get's destroyed on windows
if
log_file
!=
LOG_TO_STDERR
and
log_file
!=
LOG_TO_STDOUT
:
if
log_file
!=
LOG_TO_STDERR
and
log_file
!=
LOG_TO_STDOUT
:
log_file
=
_absolute_log_file
(
log_file
,
DEFAULT_LOG_FILE
)
log_file
=
_absolute_log_file
(
log_file
,
DEFAULT_LOG_FILE
)
previous_handler
=
r
.
handlers
[
0
]
if
r
.
handlers
else
None
previous_handler
=
r
.
handlers
[
0
]
if
r
.
handlers
else
None
...
...
cps/oauth.py
View file @
24c743d2
...
@@ -30,7 +30,7 @@ except ImportError:
...
@@ -30,7 +30,7 @@ except ImportError:
from
flask_dance.consumer.storage.sqla
import
SQLAlchemyStorage
as
SQLAlchemyBackend
from
flask_dance.consumer.storage.sqla
import
SQLAlchemyStorage
as
SQLAlchemyBackend
from
flask_dance.consumer.storage.sqla
import
first
,
_get_real_user
from
flask_dance.consumer.storage.sqla
import
first
,
_get_real_user
from
sqlalchemy.orm.exc
import
NoResultFound
from
sqlalchemy.orm.exc
import
NoResultFound
backend_resultcode
=
True
# prevent storing values with this resultcode
backend_resultcode
=
True
# prevent storing values with this resultcode
except
ImportError
:
except
ImportError
:
pass
pass
...
@@ -97,7 +97,7 @@ try:
...
@@ -97,7 +97,7 @@ try:
def
set
(
self
,
blueprint
,
token
,
user
=
None
,
user_id
=
None
):
def
set
(
self
,
blueprint
,
token
,
user
=
None
,
user_id
=
None
):
uid
=
first
([
user_id
,
self
.
user_id
,
blueprint
.
config
.
get
(
"user_id"
)])
uid
=
first
([
user_id
,
self
.
user_id
,
blueprint
.
config
.
get
(
"user_id"
)])
u
=
first
(
_get_real_user
(
ref
,
self
.
anon_user
)
u
=
first
(
_get_real_user
(
ref
,
self
.
anon_user
)
for
ref
in
(
user
,
self
.
user
,
blueprint
.
config
.
get
(
"user"
)))
for
ref
in
(
user
,
self
.
user
,
blueprint
.
config
.
get
(
"user"
)))
if
self
.
user_required
and
not
u
and
not
uid
:
if
self
.
user_required
and
not
u
and
not
uid
:
raise
ValueError
(
"Cannot set OAuth token without an associated user"
)
raise
ValueError
(
"Cannot set OAuth token without an associated user"
)
...
...
cps/opds.py
View file @
24c743d2
...
@@ -56,8 +56,8 @@ def requires_basic_auth_if_no_ano(f):
...
@@ -56,8 +56,8 @@ def requires_basic_auth_if_no_ano(f):
return
decorated
return
decorated
class
FeedObject
()
:
class
FeedObject
:
def
__init__
(
self
,
rating_id
,
rating_name
):
def
__init__
(
self
,
rating_id
,
rating_name
):
self
.
rating_id
=
rating_id
self
.
rating_id
=
rating_id
self
.
rating_name
=
rating_name
self
.
rating_name
=
rating_name
...
@@ -101,7 +101,7 @@ def feed_normal_search():
...
@@ -101,7 +101,7 @@ def feed_normal_search():
def
feed_new
():
def
feed_new
():
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
True
,
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
True
,
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -119,7 +119,8 @@ def feed_discover():
...
@@ -119,7 +119,8 @@ def feed_discover():
def
feed_best_rated
():
def
feed_best_rated
():
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
ratings
.
any
(
db
.
Ratings
.
rating
>
9
),
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
ratings
.
any
(
db
.
Ratings
.
rating
>
9
),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -153,7 +154,8 @@ def feed_hot():
...
@@ -153,7 +154,8 @@ def feed_hot():
def
feed_authorindex
():
def
feed_authorindex
():
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
=
db
.
session
.
query
(
db
.
Authors
)
.
join
(
db
.
books_authors_link
)
.
join
(
db
.
Books
)
.
filter
(
common_filters
())
\
entries
=
db
.
session
.
query
(
db
.
Authors
)
.
join
(
db
.
books_authors_link
)
.
join
(
db
.
Books
)
.
filter
(
common_filters
())
\
.
group_by
(
text
(
'books_authors_link.author'
))
.
order_by
(
db
.
Authors
.
sort
)
.
limit
(
config
.
config_books_per_page
)
.
offset
(
off
)
.
group_by
(
text
(
'books_authors_link.author'
))
.
order_by
(
db
.
Authors
.
sort
)
.
limit
(
config
.
config_books_per_page
)
\
.
offset
(
off
)
pagination
=
Pagination
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
config
.
config_books_per_page
,
pagination
=
Pagination
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
config
.
config_books_per_page
,
len
(
db
.
session
.
query
(
db
.
Authors
)
.
all
()))
len
(
db
.
session
.
query
(
db
.
Authors
)
.
all
()))
return
render_xml_template
(
'feed.xml'
,
listelements
=
entries
,
folder
=
'opds.feed_author'
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
listelements
=
entries
,
folder
=
'opds.feed_author'
,
pagination
=
pagination
)
...
@@ -164,7 +166,9 @@ def feed_authorindex():
...
@@ -164,7 +166,9 @@ def feed_authorindex():
def
feed_author
(
book_id
):
def
feed_author
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -173,7 +177,8 @@ def feed_author(book_id):
...
@@ -173,7 +177,8 @@ def feed_author(book_id):
def
feed_publisherindex
():
def
feed_publisherindex
():
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
=
db
.
session
.
query
(
db
.
Publishers
)
.
join
(
db
.
books_publishers_link
)
.
join
(
db
.
Books
)
.
filter
(
common_filters
())
\
entries
=
db
.
session
.
query
(
db
.
Publishers
)
.
join
(
db
.
books_publishers_link
)
.
join
(
db
.
Books
)
.
filter
(
common_filters
())
\
.
group_by
(
text
(
'books_publishers_link.publisher'
))
.
order_by
(
db
.
Publishers
.
sort
)
.
limit
(
config
.
config_books_per_page
)
.
offset
(
off
)
.
group_by
(
text
(
'books_publishers_link.publisher'
))
.
order_by
(
db
.
Publishers
.
sort
)
\
.
limit
(
config
.
config_books_per_page
)
.
offset
(
off
)
pagination
=
Pagination
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
config
.
config_books_per_page
,
pagination
=
Pagination
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
config
.
config_books_per_page
,
len
(
db
.
session
.
query
(
db
.
Publishers
)
.
all
()))
len
(
db
.
session
.
query
(
db
.
Publishers
)
.
all
()))
return
render_xml_template
(
'feed.xml'
,
listelements
=
entries
,
folder
=
'opds.feed_publisher'
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
listelements
=
entries
,
folder
=
'opds.feed_publisher'
,
pagination
=
pagination
)
...
@@ -184,7 +189,8 @@ def feed_publisherindex():
...
@@ -184,7 +189,8 @@ def feed_publisherindex():
def
feed_publisher
(
book_id
):
def
feed_publisher
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
publishers
.
any
(
db
.
Publishers
.
id
==
book_id
),
db
.
Books
,
db
.
Books
.
publishers
.
any
(
db
.
Publishers
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -205,7 +211,9 @@ def feed_categoryindex():
...
@@ -205,7 +211,9 @@ def feed_categoryindex():
def
feed_category
(
book_id
):
def
feed_category
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -225,9 +233,12 @@ def feed_seriesindex():
...
@@ -225,9 +233,12 @@ def feed_seriesindex():
def
feed_series
(
book_id
):
def
feed_series
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
book_id
),
[
db
.
Books
.
series_index
])
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
book_id
),
[
db
.
Books
.
series_index
])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
@
opds
.
route
(
"/opds/ratings"
)
@
opds
.
route
(
"/opds/ratings"
)
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
feed_ratingindex
():
def
feed_ratingindex
():
...
@@ -244,16 +255,18 @@ def feed_ratingindex():
...
@@ -244,16 +255,18 @@ def feed_ratingindex():
element
.
append
(
FeedObject
(
entry
[
0
]
.
id
,
"{} Stars"
.
format
(
entry
.
name
)))
element
.
append
(
FeedObject
(
entry
[
0
]
.
id
,
"{} Stars"
.
format
(
entry
.
name
)))
return
render_xml_template
(
'feed.xml'
,
listelements
=
element
,
folder
=
'opds.feed_ratings'
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
listelements
=
element
,
folder
=
'opds.feed_ratings'
,
pagination
=
pagination
)
@
opds
.
route
(
"/opds/ratings/<book_id>"
)
@
opds
.
route
(
"/opds/ratings/<book_id>"
)
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
feed_ratings
(
book_id
):
def
feed_ratings
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
ratings
.
any
(
db
.
Ratings
.
id
==
book_id
),[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
ratings
.
any
(
db
.
Ratings
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
@
opds
.
route
(
"/opds/formats"
)
@
opds
.
route
(
"/opds/formats"
)
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
feed_formatindex
():
def
feed_formatindex
():
...
@@ -274,7 +287,9 @@ def feed_formatindex():
...
@@ -274,7 +287,9 @@ def feed_formatindex():
def
feed_format
(
book_id
):
def
feed_format
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
data
.
any
(
db
.
Data
.
format
==
book_id
.
upper
()),
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
data
.
any
(
db
.
Data
.
format
==
book_id
.
upper
()),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -306,7 +321,9 @@ def feed_languagesindex():
...
@@ -306,7 +321,9 @@ def feed_languagesindex():
def
feed_languages
(
book_id
):
def
feed_languages
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
entries
,
__
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
languages
.
any
(
db
.
Languages
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
db
.
Books
,
db
.
Books
.
languages
.
any
(
db
.
Languages
.
id
==
book_id
),
[
db
.
Books
.
timestamp
.
desc
()])
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
...
@@ -326,7 +343,8 @@ def feed_shelfindex():
...
@@ -326,7 +343,8 @@ def feed_shelfindex():
def
feed_shelf
(
book_id
):
def
feed_shelf
(
book_id
):
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
if
current_user
.
is_anonymous
:
if
current_user
.
is_anonymous
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
book_id
,
not
ub
.
Shelf
.
deleted
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
is_public
==
1
,
ub
.
Shelf
.
id
==
book_id
,
not
ub
.
Shelf
.
deleted
)
.
first
()
else
:
else
:
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
or_
(
and_
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
),
ub
.
Shelf
.
id
==
book_id
),
ub
.
Shelf
.
id
==
book_id
),
...
@@ -349,11 +367,11 @@ def feed_shelf(book_id):
...
@@ -349,11 +367,11 @@ def feed_shelf(book_id):
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
@
download_required
@
download_required
def
opds_download_link
(
book_id
,
book_format
):
def
opds_download_link
(
book_id
,
book_format
):
return
get_download_link
(
book_id
,
book_format
.
lower
())
return
get_download_link
(
book_id
,
book_format
.
lower
())
@
opds
.
route
(
"/ajax/book/<string:uuid>/<library>"
)
@
opds
.
route
(
"/ajax/book/<string:uuid>/<library>"
)
@
opds
.
route
(
"/ajax/book/<string:uuid>"
,
defaults
=
{
'library'
:
""
})
@
opds
.
route
(
"/ajax/book/<string:uuid>"
,
defaults
=
{
'library'
:
""
})
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
get_metadata_calibre_companion
(
uuid
,
library
):
def
get_metadata_calibre_companion
(
uuid
,
library
):
entry
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
.
like
(
"
%
"
+
uuid
+
"
%
"
))
.
first
()
entry
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
uuid
.
like
(
"
%
"
+
uuid
+
"
%
"
))
.
first
()
...
@@ -369,16 +387,17 @@ def get_metadata_calibre_companion(uuid, library):
...
@@ -369,16 +387,17 @@ def get_metadata_calibre_companion(uuid, library):
def
feed_search
(
term
):
def
feed_search
(
term
):
if
term
:
if
term
:
term
=
term
.
strip
()
.
lower
()
term
=
term
.
strip
()
.
lower
()
entries
=
get_search_results
(
term
)
entries
=
get_search_results
(
term
)
entriescount
=
len
(
entries
)
if
len
(
entries
)
>
0
else
1
entriescount
=
len
(
entries
)
if
len
(
entries
)
>
0
else
1
pagination
=
Pagination
(
1
,
entriescount
,
entriescount
)
pagination
=
Pagination
(
1
,
entriescount
,
entriescount
)
return
render_xml_template
(
'feed.xml'
,
searchterm
=
term
,
entries
=
entries
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
searchterm
=
term
,
entries
=
entries
,
pagination
=
pagination
)
else
:
else
:
return
render_xml_template
(
'feed.xml'
,
searchterm
=
""
)
return
render_xml_template
(
'feed.xml'
,
searchterm
=
""
)
def
check_auth
(
username
,
password
):
def
check_auth
(
username
,
password
):
if
sys
.
version_info
.
major
==
3
:
if
sys
.
version_info
.
major
==
3
:
username
=
username
.
encode
(
'windows-1252'
)
username
=
username
.
encode
(
'windows-1252'
)
user
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
func
.
lower
(
ub
.
User
.
nickname
)
==
user
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
func
.
lower
(
ub
.
User
.
nickname
)
==
username
.
decode
(
'utf-8'
)
.
lower
())
.
first
()
username
.
decode
(
'utf-8'
)
.
lower
())
.
first
()
return
bool
(
user
and
check_password_hash
(
str
(
user
.
password
),
password
))
return
bool
(
user
and
check_password_hash
(
str
(
user
.
password
),
password
))
...
@@ -392,13 +411,14 @@ def authenticate():
...
@@ -392,13 +411,14 @@ def authenticate():
def
render_xml_template
(
*
args
,
**
kwargs
):
def
render_xml_template
(
*
args
,
**
kwargs
):
#ToDo: return time in current timezone similar to %z
#
ToDo: return time in current timezone similar to %z
currtime
=
datetime
.
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S+00:00"
)
currtime
=
datetime
.
datetime
.
now
()
.
strftime
(
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S+00:00"
)
xml
=
render_template
(
current_time
=
currtime
,
instance
=
config
.
config_calibre_web_title
,
*
args
,
**
kwargs
)
xml
=
render_template
(
current_time
=
currtime
,
instance
=
config
.
config_calibre_web_title
,
*
args
,
**
kwargs
)
response
=
make_response
(
xml
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/atom+xml; charset=utf-8"
response
.
headers
[
"Content-Type"
]
=
"application/atom+xml; charset=utf-8"
return
response
return
response
@
opds
.
route
(
"/opds/thumb_240_240/<book_id>"
)
@
opds
.
route
(
"/opds/thumb_240_240/<book_id>"
)
@
opds
.
route
(
"/opds/cover_240_240/<book_id>"
)
@
opds
.
route
(
"/opds/cover_240_240/<book_id>"
)
@
opds
.
route
(
"/opds/cover_90_90/<book_id>"
)
@
opds
.
route
(
"/opds/cover_90_90/<book_id>"
)
...
@@ -407,13 +427,15 @@ def render_xml_template(*args, **kwargs):
...
@@ -407,13 +427,15 @@ def render_xml_template(*args, **kwargs):
def
feed_get_cover
(
book_id
):
def
feed_get_cover
(
book_id
):
return
get_book_cover
(
book_id
)
return
get_book_cover
(
book_id
)
@
opds
.
route
(
"/opds/readbooks"
)
@
opds
.
route
(
"/opds/readbooks"
)
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
feed_read_books
():
def
feed_read_books
():
off
=
request
.
args
.
get
(
"offset"
)
or
0
off
=
request
.
args
.
get
(
"offset"
)
or
0
result
,
pagination
=
render_read_books
(
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
,
True
,
True
)
result
,
pagination
=
render_read_books
(
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
,
True
,
True
)
return
render_xml_template
(
'feed.xml'
,
entries
=
result
,
pagination
=
pagination
)
return
render_xml_template
(
'feed.xml'
,
entries
=
result
,
pagination
=
pagination
)
@
opds
.
route
(
"/opds/unreadbooks"
)
@
opds
.
route
(
"/opds/unreadbooks"
)
@
requires_basic_auth_if_no_ano
@
requires_basic_auth_if_no_ano
def
feed_unread_books
():
def
feed_unread_books
():
...
...
cps/server.py
View file @
24c743d2
...
@@ -43,7 +43,6 @@ from . import logger
...
@@ -43,7 +43,6 @@ from . import logger
log
=
logger
.
create
()
log
=
logger
.
create
()
def
_readable_listen_address
(
address
,
port
):
def
_readable_listen_address
(
address
,
port
):
if
':'
in
address
:
if
':'
in
address
:
address
=
"["
+
address
+
"]"
address
=
"["
+
address
+
"]"
...
@@ -84,7 +83,8 @@ class WebServer(object):
...
@@ -84,7 +83,8 @@ class WebServer(object):
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
self
.
ssl_args
=
dict
(
certfile
=
certfile_path
,
keyfile
=
keyfile_path
)
self
.
ssl_args
=
dict
(
certfile
=
certfile_path
,
keyfile
=
keyfile_path
)
else
:
else
:
log
.
warning
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl.'
)
log
.
warning
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. '
'Ignoring ssl.'
)
log
.
warning
(
'Cert path:
%
s'
,
certfile_path
)
log
.
warning
(
'Cert path:
%
s'
,
certfile_path
)
log
.
warning
(
'Key path:
%
s'
,
keyfile_path
)
log
.
warning
(
'Key path:
%
s'
,
keyfile_path
)
...
...
cps/services/SyncToken.py
View file @
24c743d2
...
@@ -49,10 +49,11 @@ def get_datetime_from_json(json_object, field_name):
...
@@ -49,10 +49,11 @@ def get_datetime_from_json(json_object, field_name):
return
datetime
.
min
return
datetime
.
min
class
SyncToken
()
:
class
SyncToken
:
""" The SyncToken is used to persist state accross requests.
""" The SyncToken is used to persist state accross requests.
When serialized over the response headers, the Kobo device will propagate the token onto following requests to the service.
When serialized over the response headers, the Kobo device will propagate the token onto following
As an example use-case, the SyncToken is used to detect books that have been added to the library since the last time the device synced to the server.
requests to the service. As an example use-case, the SyncToken is used to detect books that have been added
to the library since the last time the device synced to the server.
Attributes:
Attributes:
books_last_created: Datetime representing the newest book that the device knows about.
books_last_created: Datetime representing the newest book that the device knows about.
...
@@ -66,10 +67,11 @@ class SyncToken():
...
@@ -66,10 +67,11 @@ class SyncToken():
token_schema
=
{
token_schema
=
{
"type"
:
"object"
,
"type"
:
"object"
,
"properties"
:
{
"version"
:
{
"type"
:
"string"
},
"data"
:
{
"type"
:
"object"
},},
"properties"
:
{
"version"
:
{
"type"
:
"string"
},
"data"
:
{
"type"
:
"object"
},
},
}
}
# This Schema doesn't contain enough information to detect and propagate book deletions from Calibre to the device.
# This Schema doesn't contain enough information to detect and propagate book deletions from Calibre to the device.
# A potential solution might be to keep a list of all known book uuids in the token, and look for any missing from the db.
# A potential solution might be to keep a list of all known book uuids in the token, and look for any missing
# from the db.
data_schema_v1
=
{
data_schema_v1
=
{
"type"
:
"object"
,
"type"
:
"object"
,
"properties"
:
{
"properties"
:
{
...
...
cps/shelf.py
View file @
24c743d2
...
@@ -25,7 +25,7 @@ from __future__ import division, print_function, unicode_literals
...
@@ -25,7 +25,7 @@ from __future__ import division, print_function, unicode_literals
from
flask
import
Blueprint
,
request
,
flash
,
redirect
,
url_for
from
flask
import
Blueprint
,
request
,
flash
,
redirect
,
url_for
from
flask_babel
import
gettext
as
_
from
flask_babel
import
gettext
as
_
from
flask_login
import
login_required
,
current_user
from
flask_login
import
login_required
,
current_user
from
sqlalchemy.sql.expression
import
func
,
or_
,
and_
from
sqlalchemy.sql.expression
import
func
from
.
import
logger
,
ub
,
searched_ids
,
db
from
.
import
logger
,
ub
,
searched_ids
,
db
from
.web
import
render_title_template
from
.web
import
render_title_template
...
@@ -35,6 +35,7 @@ from .helper import common_filters
...
@@ -35,6 +35,7 @@ from .helper import common_filters
shelf
=
Blueprint
(
'shelf'
,
__name__
)
shelf
=
Blueprint
(
'shelf'
,
__name__
)
log
=
logger
.
create
()
log
=
logger
.
create
()
def
check_shelf_edit_permissions
(
cur_shelf
):
def
check_shelf_edit_permissions
(
cur_shelf
):
if
not
cur_shelf
.
is_public
and
not
cur_shelf
.
user_id
==
int
(
current_user
.
id
):
if
not
cur_shelf
.
is_public
and
not
cur_shelf
.
user_id
==
int
(
current_user
.
id
):
log
.
error
(
"User
%
s not allowed to edit shelf
%
s"
,
current_user
,
cur_shelf
)
log
.
error
(
"User
%
s not allowed to edit shelf
%
s"
,
current_user
,
cur_shelf
)
...
@@ -74,7 +75,7 @@ def add_to_shelf(shelf_id, book_id):
...
@@ -74,7 +75,7 @@ def add_to_shelf(shelf_id, book_id):
return
"Sorry you are not allowed to add a book to the the shelf:
%
s"
%
shelf
.
name
,
403
return
"Sorry you are not allowed to add a book to the the shelf:
%
s"
%
shelf
.
name
,
403
book_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
book_in_shelf
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
,
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
ub
.
BookShelf
.
book_id
==
book_id
)
.
first
()
if
book_in_shelf
:
if
book_in_shelf
:
log
.
error
(
"Book
%
s is already part of
%
s"
,
book_id
,
shelf
)
log
.
error
(
"Book
%
s is already part of
%
s"
,
book_id
,
shelf
)
if
not
xhr
:
if
not
xhr
:
...
@@ -195,7 +196,6 @@ def remove_from_shelf(shelf_id, book_id):
...
@@ -195,7 +196,6 @@ def remove_from_shelf(shelf_id, book_id):
return
"Sorry you are not allowed to remove a book from this shelf:
%
s"
%
shelf
.
name
,
403
return
"Sorry you are not allowed to remove a book from this shelf:
%
s"
%
shelf
.
name
,
403
@
shelf
.
route
(
"/shelf/create"
,
methods
=
[
"GET"
,
"POST"
])
@
shelf
.
route
(
"/shelf/create"
,
methods
=
[
"GET"
,
"POST"
])
@
login_required
@
login_required
def
create_shelf
():
def
create_shelf
():
...
@@ -214,21 +214,24 @@ def create_shelf():
...
@@ -214,21 +214,24 @@ def create_shelf():
.
first
()
is
None
.
first
()
is
None
if
not
is_shelf_name_unique
:
if
not
is_shelf_name_unique
:
flash
(
_
(
u"A public shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
flash
(
_
(
u"A public shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
else
:
else
:
is_shelf_name_unique
=
ub
.
session
.
query
(
ub
.
Shelf
)
\
is_shelf_name_unique
=
ub
.
session
.
query
(
ub
.
Shelf
)
\
.
filter
((
ub
.
Shelf
.
name
==
to_save
[
"title"
])
&
(
ub
.
Shelf
.
is_public
==
0
)
&
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
)))
\
.
filter
((
ub
.
Shelf
.
name
==
to_save
[
"title"
])
&
(
ub
.
Shelf
.
is_public
==
0
)
&
.
first
()
is
None
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
)))
\
.
first
()
is
None
if
not
is_shelf_name_unique
:
if
not
is_shelf_name_unique
:
flash
(
_
(
u"A private shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
flash
(
_
(
u"A private shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
if
is_shelf_name_unique
:
if
is_shelf_name_unique
:
try
:
try
:
ub
.
session
.
add
(
shelf
)
ub
.
session
.
add
(
shelf
)
ub
.
session
.
commit
()
ub
.
session
.
commit
()
flash
(
_
(
u"Shelf
%(title)
s created"
,
title
=
to_save
[
"title"
]),
category
=
"success"
)
flash
(
_
(
u"Shelf
%(title)
s created"
,
title
=
to_save
[
"title"
]),
category
=
"success"
)
return
redirect
(
url_for
(
'shelf.show_shelf'
,
shelf_id
=
shelf
.
id
))
return
redirect
(
url_for
(
'shelf.show_shelf'
,
shelf_id
=
shelf
.
id
))
except
Exception
:
except
Exception
:
flash
(
_
(
u"There was an error"
),
category
=
"error"
)
flash
(
_
(
u"There was an error"
),
category
=
"error"
)
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"Create a Shelf"
),
page
=
"shelfcreate"
)
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"Create a Shelf"
),
page
=
"shelfcreate"
)
...
@@ -240,7 +243,7 @@ def create_shelf():
...
@@ -240,7 +243,7 @@ def create_shelf():
@
login_required
@
login_required
def
edit_shelf
(
shelf_id
):
def
edit_shelf
(
shelf_id
):
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
shelf
=
ub
.
session
.
query
(
ub
.
Shelf
)
.
filter
(
ub
.
Shelf
.
id
==
shelf_id
)
.
first
()
if
request
.
method
==
"POST"
:
if
request
.
method
==
"POST"
:
to_save
=
request
.
form
.
to_dict
()
to_save
=
request
.
form
.
to_dict
()
is_shelf_name_unique
=
False
is_shelf_name_unique
=
False
...
@@ -251,15 +254,18 @@ def edit_shelf(shelf_id):
...
@@ -251,15 +254,18 @@ def edit_shelf(shelf_id):
.
first
()
is
None
.
first
()
is
None
if
not
is_shelf_name_unique
:
if
not
is_shelf_name_unique
:
flash
(
_
(
u"A public shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
flash
(
_
(
u"A public shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
else
:
else
:
is_shelf_name_unique
=
ub
.
session
.
query
(
ub
.
Shelf
)
\
is_shelf_name_unique
=
ub
.
session
.
query
(
ub
.
Shelf
)
\
.
filter
((
ub
.
Shelf
.
name
==
to_save
[
"title"
])
&
(
ub
.
Shelf
.
is_public
==
0
)
&
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
)))
\
.
filter
((
ub
.
Shelf
.
name
==
to_save
[
"title"
])
&
(
ub
.
Shelf
.
is_public
==
0
)
&
.
filter
(
ub
.
Shelf
.
id
!=
shelf_id
)
\
(
ub
.
Shelf
.
user_id
==
int
(
current_user
.
id
)))
\
.
first
()
is
None
.
filter
(
ub
.
Shelf
.
id
!=
shelf_id
)
\
.
first
()
is
None
if
not
is_shelf_name_unique
:
if
not
is_shelf_name_unique
:
flash
(
_
(
u"A private shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
flash
(
_
(
u"A private shelf with the name '
%(title)
s' already exists."
,
title
=
to_save
[
"title"
]),
category
=
"error"
)
if
is_shelf_name_unique
:
if
is_shelf_name_unique
:
shelf
.
name
=
to_save
[
"title"
]
shelf
.
name
=
to_save
[
"title"
]
...
@@ -283,7 +289,7 @@ def delete_shelf_helper(cur_shelf):
...
@@ -283,7 +289,7 @@ def delete_shelf_helper(cur_shelf):
shelf_id
=
cur_shelf
.
id
shelf_id
=
cur_shelf
.
id
ub
.
session
.
delete
(
cur_shelf
)
ub
.
session
.
delete
(
cur_shelf
)
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
delete
()
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
shelf
==
shelf_id
)
.
delete
()
ub
.
session
.
add
(
ub
.
ShelfArchive
(
uuid
=
cur_shelf
.
uuid
,
user_id
=
cur_shelf
.
uuid
))
ub
.
session
.
add
(
ub
.
ShelfArchive
(
uuid
=
cur_shelf
.
uuid
,
user_id
=
cur_shelf
.
uuid
))
ub
.
session
.
commit
()
ub
.
session
.
commit
()
log
.
info
(
"successfully deleted
%
s"
,
cur_shelf
)
log
.
info
(
"successfully deleted
%
s"
,
cur_shelf
)
...
@@ -295,7 +301,7 @@ def delete_shelf(shelf_id):
...
@@ -295,7 +301,7 @@ def delete_shelf(shelf_id):
delete_shelf_helper
(
cur_shelf
)
delete_shelf_helper
(
cur_shelf
)
return
redirect
(
url_for
(
'web.index'
))
return
redirect
(
url_for
(
'web.index'
))
# @shelf.route("/shelfdown/<int:shelf_id>")
@
shelf
.
route
(
"/shelf/<int:shelf_id>"
,
defaults
=
{
'shelf_type'
:
1
})
@
shelf
.
route
(
"/shelf/<int:shelf_id>"
,
defaults
=
{
'shelf_type'
:
1
})
@
shelf
.
route
(
"/shelf/<int:shelf_id>/<int:shelf_type>"
)
@
shelf
.
route
(
"/shelf/<int:shelf_id>/<int:shelf_type>"
)
def
show_shelf
(
shelf_type
,
shelf_id
):
def
show_shelf
(
shelf_type
,
shelf_id
):
...
@@ -319,13 +325,12 @@ def show_shelf(shelf_type, shelf_id):
...
@@ -319,13 +325,12 @@ def show_shelf(shelf_type, shelf_id):
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
book_id
==
book
.
book_id
)
.
delete
()
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
book_id
==
book
.
book_id
)
.
delete
()
ub
.
session
.
commit
()
ub
.
session
.
commit
()
return
render_title_template
(
page
,
entries
=
result
,
title
=
_
(
u"Shelf: '
%(name)
s'"
,
name
=
shelf
.
name
),
return
render_title_template
(
page
,
entries
=
result
,
title
=
_
(
u"Shelf: '
%(name)
s'"
,
name
=
shelf
.
name
),
shelf
=
shelf
,
page
=
"shelf"
)
shelf
=
shelf
,
page
=
"shelf"
)
else
:
else
:
flash
(
_
(
u"Error opening shelf. Shelf does not exist or is not accessible"
),
category
=
"error"
)
flash
(
_
(
u"Error opening shelf. Shelf does not exist or is not accessible"
),
category
=
"error"
)
return
redirect
(
url_for
(
"web.index"
))
return
redirect
(
url_for
(
"web.index"
))
@
shelf
.
route
(
"/shelf/order/<int:shelf_id>"
,
methods
=
[
"GET"
,
"POST"
])
@
shelf
.
route
(
"/shelf/order/<int:shelf_id>"
,
methods
=
[
"GET"
,
"POST"
])
@
login_required
@
login_required
def
order_shelf
(
shelf_id
):
def
order_shelf
(
shelf_id
):
...
@@ -347,17 +352,17 @@ def order_shelf(shelf_id):
...
@@ -347,17 +352,17 @@ def order_shelf(shelf_id):
for
book
in
books_in_shelf2
:
for
book
in
books_in_shelf2
:
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
book_id
)
.
filter
(
common_filters
())
.
first
()
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
book_id
)
.
filter
(
common_filters
())
.
first
()
if
cur_book
:
if
cur_book
:
result
.
append
({
'title'
:
cur_book
.
title
,
result
.
append
({
'title'
:
cur_book
.
title
,
'id'
:
cur_book
.
id
,
'id'
:
cur_book
.
id
,
'author'
:
cur_book
.
authors
,
'author'
:
cur_book
.
authors
,
'series'
:
cur_book
.
series
,
'series'
:
cur_book
.
series
,
'series_index'
:
cur_book
.
series_index
})
'series_index'
:
cur_book
.
series_index
})
else
:
else
:
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
book_id
)
.
first
()
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
book_id
)
.
first
()
result
.
append
({
'title'
:
_
(
'Hidden Book'
),
result
.
append
({
'title'
:
_
(
'Hidden Book'
),
'id'
:
cur_book
.
id
,
'id'
:
cur_book
.
id
,
'author'
:[],
'author'
:
[],
'series'
:[]})
'series'
:
[]})
return
render_title_template
(
'shelf_order.html'
,
entries
=
result
,
return
render_title_template
(
'shelf_order.html'
,
entries
=
result
,
title
=
_
(
u"Change order of Shelf: '
%(name)
s'"
,
name
=
shelf
.
name
),
title
=
_
(
u"Change order of Shelf: '
%(name)
s'"
,
name
=
shelf
.
name
),
shelf
=
shelf
,
page
=
"shelforder"
)
shelf
=
shelf
,
page
=
"shelforder"
)
cps/subproc_wrapper.py
View file @
24c743d2
...
@@ -45,10 +45,10 @@ def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subpro
...
@@ -45,10 +45,10 @@ def process_open(command, quotes=(), env=None, sout=subprocess.PIPE, serr=subpro
def
process_wait
(
command
,
serr
=
subprocess
.
PIPE
):
def
process_wait
(
command
,
serr
=
subprocess
.
PIPE
):
'''Run command, wait for process to terminate, and return an iterator over lines of its output.'''
# Run command, wait for process to terminate, and return an iterator over lines of its output.
p
=
process_open
(
command
,
serr
=
serr
)
p
=
process_open
(
command
,
serr
=
serr
)
p
.
wait
()
p
.
wait
()
for
l
in
p
.
stdout
.
readlines
():
for
l
ine
in
p
.
stdout
.
readlines
():
if
isinstance
(
l
,
bytes
):
if
isinstance
(
l
ine
,
bytes
):
l
=
l
.
decode
(
'utf-8'
)
l
ine
=
line
.
decode
(
'utf-8'
)
yield
l
yield
l
ine
cps/templates/list.html
View file @
24c743d2
...
@@ -4,18 +4,18 @@
...
@@ -4,18 +4,18 @@
<div
class=
"filterheader hidden-xs hidden-sm"
>
<div
class=
"filterheader hidden-xs hidden-sm"
>
{% if entries.__len__() %}
{% if entries.__len__() %}
{% if
entries[0][0].sort
%}
{% if
data == 'author'
%}
<button
id=
"sort_name"
class=
"btn btn-
success
"
><b>
B,A
<->
A B
</b></button>
<button
id=
"sort_name"
class=
"btn btn-
primary
"
><b>
B,A
<->
A B
</b></button>
{% endif %}
{% endif %}
{% endif %}
{% endif %}
<button
id=
"desc"
class=
"btn btn-
success
"
><span
class=
"glyphicon glyphicon-sort-by-alphabet"
></span></button>
<button
id=
"desc"
class=
"btn btn-
primary
"
><span
class=
"glyphicon glyphicon-sort-by-alphabet"
></span></button>
<button
id=
"asc"
class=
"btn btn-
success
"
><span
class=
"glyphicon glyphicon-sort-by-alphabet-alt"
></span></button>
<button
id=
"asc"
class=
"btn btn-
primary
"
><span
class=
"glyphicon glyphicon-sort-by-alphabet-alt"
></span></button>
{% if charlist|length %}
{% if charlist|length %}
<button
id=
"all"
class=
"btn btn-
success
"
>
{{_('All')}}
</button>
<button
id=
"all"
class=
"btn btn-
primary
"
>
{{_('All')}}
</button>
{% endif %}
{% endif %}
<div
class=
"btn-group character"
role=
"group"
>
<div
class=
"btn-group character"
role=
"group"
>
{% for char in charlist%}
{% for char in charlist%}
<button
class=
"btn btn-
success
char"
>
{{char.char}}
</button>
<button
class=
"btn btn-
primary
char"
>
{{char.char}}
</button>
{% endfor %}
{% endfor %}
</div>
</div>
</div>
</div>
...
...
cps/ub.py
View file @
24c743d2
...
@@ -42,11 +42,10 @@ from sqlalchemy import create_engine, exc, exists, event
...
@@ -42,11 +42,10 @@ from sqlalchemy import create_engine, exc, exists, event
from
sqlalchemy
import
Column
,
ForeignKey
from
sqlalchemy
import
Column
,
ForeignKey
from
sqlalchemy
import
String
,
Integer
,
SmallInteger
,
Boolean
,
DateTime
,
Float
from
sqlalchemy
import
String
,
Integer
,
SmallInteger
,
Boolean
,
DateTime
,
Float
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.orm
import
backref
,
foreign
,
relationship
,
remote
,
sessionmaker
,
Session
from
sqlalchemy.orm
import
backref
,
relationship
,
sessionmaker
,
Session
from
sqlalchemy.sql.expression
import
and_
from
werkzeug.security
import
generate_password_hash
from
werkzeug.security
import
generate_password_hash
from
.
import
constants
# , config
from
.
import
constants
session
=
None
session
=
None
...
@@ -57,39 +56,39 @@ def get_sidebar_config(kwargs=None):
...
@@ -57,39 +56,39 @@ def get_sidebar_config(kwargs=None):
kwargs
=
kwargs
or
[]
kwargs
=
kwargs
or
[]
if
'content'
in
kwargs
:
if
'content'
in
kwargs
:
content
=
kwargs
[
'content'
]
content
=
kwargs
[
'content'
]
content
=
isinstance
(
content
,
(
User
,
LocalProxy
))
and
not
content
.
role_anonymous
()
content
=
isinstance
(
content
,
(
User
,
LocalProxy
))
and
not
content
.
role_anonymous
()
else
:
else
:
content
=
'conf'
in
kwargs
content
=
'conf'
in
kwargs
sidebar
=
list
()
sidebar
=
list
()
sidebar
.
append
({
"glyph"
:
"glyphicon-book"
,
"text"
:
_
(
'Recently Added'
),
"link"
:
'web.index'
,
"id"
:
"new"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-book"
,
"text"
:
_
(
'Recently Added'
),
"link"
:
'web.index'
,
"id"
:
"new"
,
"visibility"
:
constants
.
SIDEBAR_RECENT
,
'public'
:
True
,
"page"
:
"root"
,
"visibility"
:
constants
.
SIDEBAR_RECENT
,
'public'
:
True
,
"page"
:
"root"
,
"show_text"
:
_
(
'Show recent books'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show recent books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-fire"
,
"text"
:
_
(
'Hot Books'
),
"link"
:
'web.books_list'
,
"id"
:
"hot"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-fire"
,
"text"
:
_
(
'Hot Books'
),
"link"
:
'web.books_list'
,
"id"
:
"hot"
,
"visibility"
:
constants
.
SIDEBAR_HOT
,
'public'
:
True
,
"page"
:
"hot"
,
"show_text"
:
_
(
'Show Hot Books'
),
"visibility"
:
constants
.
SIDEBAR_HOT
,
'public'
:
True
,
"page"
:
"hot"
,
"
config_show"
:
True
})
"
show_text"
:
_
(
'Show Hot Books'
),
"config_show"
:
True
})
sidebar
.
append
(
sidebar
.
append
(
{
"glyph"
:
"glyphicon-star"
,
"text"
:
_
(
'Top Rated Books'
),
"link"
:
'web.books_list'
,
"id"
:
"rated"
,
{
"glyph"
:
"glyphicon-star"
,
"text"
:
_
(
'Top Rated Books'
),
"link"
:
'web.books_list'
,
"id"
:
"rated"
,
"visibility"
:
constants
.
SIDEBAR_BEST_RATED
,
'public'
:
True
,
"page"
:
"rated"
,
"visibility"
:
constants
.
SIDEBAR_BEST_RATED
,
'public'
:
True
,
"page"
:
"rated"
,
"show_text"
:
_
(
'Show Top Rated Books'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show Top Rated Books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-eye-open"
,
"text"
:
_
(
'Read Books'
),
"link"
:
'web.books_list'
,
"id"
:
"read"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-eye-open"
,
"text"
:
_
(
'Read Books'
),
"link"
:
'web.books_list'
,
"id"
:
"read"
,
"visibility"
:
constants
.
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"read"
,
"visibility"
:
constants
.
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"read"
,
"show_text"
:
_
(
'Show read and unread'
),
"config_show"
:
content
})
"show_text"
:
_
(
'Show read and unread'
),
"config_show"
:
content
})
sidebar
.
append
(
sidebar
.
append
(
{
"glyph"
:
"glyphicon-eye-close"
,
"text"
:
_
(
'Unread Books'
),
"link"
:
'web.books_list'
,
"id"
:
"unread"
,
{
"glyph"
:
"glyphicon-eye-close"
,
"text"
:
_
(
'Unread Books'
),
"link"
:
'web.books_list'
,
"id"
:
"unread"
,
"visibility"
:
constants
.
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"unread"
,
"visibility"
:
constants
.
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"unread"
,
"show_text"
:
_
(
'Show unread'
),
"config_show"
:
False
})
"show_text"
:
_
(
'Show unread'
),
"config_show"
:
False
})
sidebar
.
append
({
"glyph"
:
"glyphicon-random"
,
"text"
:
_
(
'Discover'
),
"link"
:
'web.books_list'
,
"id"
:
"rand"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-random"
,
"text"
:
_
(
'Discover'
),
"link"
:
'web.books_list'
,
"id"
:
"rand"
,
"visibility"
:
constants
.
SIDEBAR_RANDOM
,
'public'
:
True
,
"page"
:
"discover"
,
"visibility"
:
constants
.
SIDEBAR_RANDOM
,
'public'
:
True
,
"page"
:
"discover"
,
"show_text"
:
_
(
'Show random books'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show random books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-inbox"
,
"text"
:
_
(
'Categories'
),
"link"
:
'web.category_list'
,
"id"
:
"cat"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-inbox"
,
"text"
:
_
(
'Categories'
),
"link"
:
'web.category_list'
,
"id"
:
"cat"
,
"visibility"
:
constants
.
SIDEBAR_CATEGORY
,
'public'
:
True
,
"page"
:
"category"
,
"visibility"
:
constants
.
SIDEBAR_CATEGORY
,
'public'
:
True
,
"page"
:
"category"
,
"show_text"
:
_
(
'Show category selection'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show category selection'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-bookmark"
,
"text"
:
_
(
'Series'
),
"link"
:
'web.series_list'
,
"id"
:
"serie"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-bookmark"
,
"text"
:
_
(
'Series'
),
"link"
:
'web.series_list'
,
"id"
:
"serie"
,
"visibility"
:
constants
.
SIDEBAR_SERIES
,
'public'
:
True
,
"page"
:
"series"
,
"visibility"
:
constants
.
SIDEBAR_SERIES
,
'public'
:
True
,
"page"
:
"series"
,
"show_text"
:
_
(
'Show series selection'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show series selection'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-user"
,
"text"
:
_
(
'Authors'
),
"link"
:
'web.author_list'
,
"id"
:
"author"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-user"
,
"text"
:
_
(
'Authors'
),
"link"
:
'web.author_list'
,
"id"
:
"author"
,
"visibility"
:
constants
.
SIDEBAR_AUTHOR
,
'public'
:
True
,
"page"
:
"author"
,
"visibility"
:
constants
.
SIDEBAR_AUTHOR
,
'public'
:
True
,
"page"
:
"author"
,
"show_text"
:
_
(
'Show author selection'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show author selection'
),
"config_show"
:
True
})
sidebar
.
append
(
sidebar
.
append
(
{
"glyph"
:
"glyphicon-text-size"
,
"text"
:
_
(
'Publishers'
),
"link"
:
'web.publisher_list'
,
"id"
:
"publisher"
,
{
"glyph"
:
"glyphicon-text-size"
,
"text"
:
_
(
'Publishers'
),
"link"
:
'web.publisher_list'
,
"id"
:
"publisher"
,
"visibility"
:
constants
.
SIDEBAR_PUBLISHER
,
'public'
:
True
,
"page"
:
"publisher"
,
"visibility"
:
constants
.
SIDEBAR_PUBLISHER
,
'public'
:
True
,
"page"
:
"publisher"
,
...
@@ -97,13 +96,13 @@ def get_sidebar_config(kwargs=None):
...
@@ -97,13 +96,13 @@ def get_sidebar_config(kwargs=None):
sidebar
.
append
({
"glyph"
:
"glyphicon-flag"
,
"text"
:
_
(
'Languages'
),
"link"
:
'web.language_overview'
,
"id"
:
"lang"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-flag"
,
"text"
:
_
(
'Languages'
),
"link"
:
'web.language_overview'
,
"id"
:
"lang"
,
"visibility"
:
constants
.
SIDEBAR_LANGUAGE
,
'public'
:
(
g
.
user
.
filter_language
()
==
'all'
),
"visibility"
:
constants
.
SIDEBAR_LANGUAGE
,
'public'
:
(
g
.
user
.
filter_language
()
==
'all'
),
"page"
:
"language"
,
"page"
:
"language"
,
"show_text"
:
_
(
'Show language selection'
),
"config_show"
:
True
})
"show_text"
:
_
(
'Show language selection'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-star-empty"
,
"text"
:
_
(
'Ratings'
),
"link"
:
'web.ratings_list'
,
"id"
:
"rate"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-star-empty"
,
"text"
:
_
(
'Ratings'
),
"link"
:
'web.ratings_list'
,
"id"
:
"rate"
,
"visibility"
:
constants
.
SIDEBAR_RATING
,
'public'
:
True
,
"visibility"
:
constants
.
SIDEBAR_RATING
,
'public'
:
True
,
"page"
:
"rating"
,
"show_text"
:
_
(
'Show ratings selection'
),
"config_show"
:
True
})
"page"
:
"rating"
,
"show_text"
:
_
(
'Show ratings selection'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-file"
,
"text"
:
_
(
'File formats'
),
"link"
:
'web.formats_list'
,
"id"
:
"format"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-file"
,
"text"
:
_
(
'File formats'
),
"link"
:
'web.formats_list'
,
"id"
:
"format"
,
"visibility"
:
constants
.
SIDEBAR_FORMAT
,
'public'
:
True
,
"visibility"
:
constants
.
SIDEBAR_FORMAT
,
'public'
:
True
,
"page"
:
"format"
,
"show_text"
:
_
(
'Show file formats selection'
),
"config_show"
:
True
})
"page"
:
"format"
,
"show_text"
:
_
(
'Show file formats selection'
),
"config_show"
:
True
})
sidebar
.
append
(
sidebar
.
append
(
{
"glyph"
:
"glyphicon-trash"
,
"text"
:
_
(
'Archived Books'
),
"link"
:
'web.books_list'
,
"id"
:
"archived"
,
{
"glyph"
:
"glyphicon-trash"
,
"text"
:
_
(
'Archived Books'
),
"link"
:
'web.books_list'
,
"id"
:
"archived"
,
"visibility"
:
constants
.
SIDEBAR_ARCHIVED
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"archived"
,
"visibility"
:
constants
.
SIDEBAR_ARCHIVED
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"archived"
,
...
@@ -236,7 +235,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
...
@@ -236,7 +235,8 @@ class Anonymous(AnonymousUserMixin, UserBase):
self
.
loadSettings
()
self
.
loadSettings
()
def
loadSettings
(
self
):
def
loadSettings
(
self
):
data
=
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
.
first
()
# type: User
data
=
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
\
.
first
()
# type: User
self
.
nickname
=
data
.
nickname
self
.
nickname
=
data
.
nickname
self
.
role
=
data
.
role
self
.
role
=
data
.
role
self
.
id
=
data
.
id
self
.
id
=
data
.
id
...
@@ -259,7 +259,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
...
@@ -259,7 +259,7 @@ class Anonymous(AnonymousUserMixin, UserBase):
@
property
@
property
def
is_anonymous
(
self
):
def
is_anonymous
(
self
):
return
True
# self.anon_browse
return
True
@
property
@
property
def
is_authenticated
(
self
):
def
is_authenticated
(
self
):
...
@@ -271,7 +271,7 @@ class Shelf(Base):
...
@@ -271,7 +271,7 @@ class Shelf(Base):
__tablename__
=
'shelf'
__tablename__
=
'shelf'
id
=
Column
(
Integer
,
primary_key
=
True
)
id
=
Column
(
Integer
,
primary_key
=
True
)
uuid
=
Column
(
String
,
default
=
lambda
:
str
(
uuid
.
uuid4
()))
uuid
=
Column
(
String
,
default
=
lambda
:
str
(
uuid
.
uuid4
()))
name
=
Column
(
String
)
name
=
Column
(
String
)
is_public
=
Column
(
Integer
,
default
=
0
)
is_public
=
Column
(
Integer
,
default
=
0
)
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
))
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
))
...
@@ -318,8 +318,12 @@ class ReadBook(Base):
...
@@ -318,8 +318,12 @@ class ReadBook(Base):
book_id
=
Column
(
Integer
,
unique
=
False
)
book_id
=
Column
(
Integer
,
unique
=
False
)
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
),
unique
=
False
)
user_id
=
Column
(
Integer
,
ForeignKey
(
'user.id'
),
unique
=
False
)
read_status
=
Column
(
Integer
,
unique
=
False
,
default
=
STATUS_UNREAD
,
nullable
=
False
)
read_status
=
Column
(
Integer
,
unique
=
False
,
default
=
STATUS_UNREAD
,
nullable
=
False
)
kobo_reading_state
=
relationship
(
"KoboReadingState"
,
uselist
=
False
,
primaryjoin
=
"and_(ReadBook.user_id == foreign(KoboReadingState.user_id), "
kobo_reading_state
=
relationship
(
"KoboReadingState"
,
uselist
=
False
,
"ReadBook.book_id == foreign(KoboReadingState.book_id))"
,
cascade
=
"all"
,
backref
=
backref
(
"book_read_link"
,
uselist
=
False
))
primaryjoin
=
"and_(ReadBook.user_id == foreign(KoboReadingState.user_id), "
"ReadBook.book_id == foreign(KoboReadingState.book_id))"
,
cascade
=
"all"
,
backref
=
backref
(
"book_read_link"
,
uselist
=
False
))
last_modified
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
,
onupdate
=
datetime
.
datetime
.
utcnow
)
last_modified
=
Column
(
DateTime
,
default
=
datetime
.
datetime
.
utcnow
,
onupdate
=
datetime
.
datetime
.
utcnow
)
last_time_started_reading
=
Column
(
DateTime
,
nullable
=
True
)
last_time_started_reading
=
Column
(
DateTime
,
nullable
=
True
)
times_started_reading
=
Column
(
Integer
,
default
=
0
,
nullable
=
False
)
times_started_reading
=
Column
(
Integer
,
default
=
0
,
nullable
=
False
)
...
@@ -334,6 +338,7 @@ class Bookmark(Base):
...
@@ -334,6 +338,7 @@ class Bookmark(Base):
format
=
Column
(
String
(
collation
=
'NOCASE'
))
format
=
Column
(
String
(
collation
=
'NOCASE'
))
bookmark_key
=
Column
(
String
)
bookmark_key
=
Column
(
String
)
# Baseclass representing books that are archived on the user's Kobo device.
# Baseclass representing books that are archived on the user's Kobo device.
class
ArchivedBook
(
Base
):
class
ArchivedBook
(
Base
):
__tablename__
=
'archived_book'
__tablename__
=
'archived_book'
...
@@ -421,7 +426,6 @@ class Registration(Base):
...
@@ -421,7 +426,6 @@ class Registration(Base):
return
u"<Registration('{0}')>"
.
format
(
self
.
domain
)
return
u"<Registration('{0}')>"
.
format
(
self
.
domain
)
class
RemoteAuthToken
(
Base
):
class
RemoteAuthToken
(
Base
):
__tablename__
=
'remote_auth_token'
__tablename__
=
'remote_auth_token'
...
@@ -486,7 +490,7 @@ def migrate_Database(session):
...
@@ -486,7 +490,7 @@ def migrate_Database(session):
conn
.
execute
(
"UPDATE book_read_link SET 'read_status' = 1 WHERE is_read"
)
conn
.
execute
(
"UPDATE book_read_link SET 'read_status' = 1 WHERE is_read"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_modified' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_modified' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'last_time_started_reading' DATETIME"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
conn
.
execute
(
"ALTER TABLE book_read_link ADD column 'times_started_reading' INTEGER DEFAULT 0"
)
session
.
commit
()
session
.
commit
()
try
:
try
:
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
Shelf
.
uuid
))
.
scalar
()
...
@@ -531,19 +535,13 @@ def migrate_Database(session):
...
@@ -531,19 +535,13 @@ def migrate_Database(session):
except
exc
.
OperationalError
:
except
exc
.
OperationalError
:
conn
=
engine
.
connect
()
conn
=
engine
.
connect
()
conn
.
execute
(
"UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang "
conn
.
execute
(
"UPDATE user SET 'sidebar_view' = (random_books* :side_random + language_books * :side_lang "
"+ series_books * :side_series + category_books * :side_category + hot_books * "
"+ series_books * :side_series + category_books * :side_category + hot_books * "
":side_hot + :side_autor + :detail_random)"
":side_hot + :side_autor + :detail_random)"
,
,{
'side_random'
:
constants
.
SIDEBAR_RANDOM
,
'side_lang'
:
constants
.
SIDEBAR_LANGUAGE
,
{
'side_random'
:
constants
.
SIDEBAR_RANDOM
,
'side_lang'
:
constants
.
SIDEBAR_LANGUAGE
,
'side_series'
:
constants
.
SIDEBAR_SERIES
,
'side_series'
:
constants
.
SIDEBAR_SERIES
,
'side_category'
:
constants
.
SIDEBAR_CATEGORY
,
'side_category'
:
constants
.
SIDEBAR_CATEGORY
,
'side_hot'
:
constants
.
SIDEBAR_HOT
,
'side_hot'
:
constants
.
SIDEBAR_HOT
,
'side_autor'
:
constants
.
SIDEBAR_AUTHOR
,
'side_autor'
:
constants
.
SIDEBAR_AUTHOR
,
'detail_random'
:
constants
.
DETAIL_RANDOM
})
'detail_random'
:
constants
.
DETAIL_RANDOM
})
session
.
commit
()
session
.
commit
()
'''try:
session.query(exists().where(User.mature_content)).scalar()
except exc.OperationalError:
conn = engine.connect()
conn.execute("ALTER TABLE user ADD column `mature_content` INTEGER DEFAULT 1")'''
try
:
try
:
session
.
query
(
exists
()
.
where
(
User
.
denied_tags
))
.
scalar
()
session
.
query
(
exists
()
.
where
(
User
.
denied_tags
))
.
scalar
()
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
except
exc
.
OperationalError
:
# Database is not compatible, some columns are missing
...
@@ -552,7 +550,8 @@ def migrate_Database(session):
...
@@ -552,7 +550,8 @@ def migrate_Database(session):
conn
.
execute
(
"ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''"
)
conn
.
execute
(
"ALTER TABLE user ADD column `allowed_tags` String DEFAULT ''"
)
conn
.
execute
(
"ALTER TABLE user ADD column `denied_column_value` DEFAULT ''"
)
conn
.
execute
(
"ALTER TABLE user ADD column `denied_column_value` DEFAULT ''"
)
conn
.
execute
(
"ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''"
)
conn
.
execute
(
"ALTER TABLE user ADD column `allowed_column_value` DEFAULT ''"
)
if
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
.
first
()
is
None
:
if
session
.
query
(
User
)
.
filter
(
User
.
role
.
op
(
'&'
)(
constants
.
ROLE_ANONYMOUS
)
==
constants
.
ROLE_ANONYMOUS
)
.
first
()
\
is
None
:
create_anonymous_user
(
session
)
create_anonymous_user
(
session
)
try
:
try
:
# check if one table with autoincrement is existing (should be user table)
# check if one table with autoincrement is existing (should be user table)
...
@@ -562,20 +561,20 @@ def migrate_Database(session):
...
@@ -562,20 +561,20 @@ def migrate_Database(session):
# Create new table user_id and copy contents of table user into it
# Create new table user_id and copy contents of table user into it
conn
=
engine
.
connect
()
conn
=
engine
.
connect
()
conn
.
execute
(
"CREATE TABLE user_id (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
conn
.
execute
(
"CREATE TABLE user_id (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
"
nickname VARCHAR(64),"
"
nickname VARCHAR(64),"
"email VARCHAR(120),"
"email VARCHAR(120),"
"role SMALLINT,"
"role SMALLINT,"
"password VARCHAR,"
"password VARCHAR,"
"kindle_mail VARCHAR(120),"
"kindle_mail VARCHAR(120),"
"locale VARCHAR(2),"
"locale VARCHAR(2),"
"sidebar_view INTEGER,"
"sidebar_view INTEGER,"
"default_language VARCHAR(3),"
"default_language VARCHAR(3),"
"UNIQUE (nickname),"
"UNIQUE (nickname),"
"UNIQUE (email))"
)
"UNIQUE (email))"
)
conn
.
execute
(
"INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
conn
.
execute
(
"INSERT INTO user_id(id, nickname, email, role, password, kindle_mail,locale,"
"sidebar_view, default_language) "
"sidebar_view, default_language) "
"SELECT id, nickname, email, role, password, kindle_mail, locale,"
"SELECT id, nickname, email, role, password, kindle_mail, locale,"
"sidebar_view, default_language FROM user"
)
"sidebar_view, default_language FROM user"
)
# delete old user table and rename new user_id table to user:
# delete old user table and rename new user_id table to user:
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"DROP TABLE user"
)
conn
.
execute
(
"ALTER TABLE user_id RENAME TO user"
)
conn
.
execute
(
"ALTER TABLE user_id RENAME TO user"
)
...
@@ -591,25 +590,26 @@ def clean_database(session):
...
@@ -591,25 +590,26 @@ def clean_database(session):
# Remove expired remote login tokens
# Remove expired remote login tokens
now
=
datetime
.
datetime
.
now
()
now
=
datetime
.
datetime
.
now
()
session
.
query
(
RemoteAuthToken
)
.
filter
(
now
>
RemoteAuthToken
.
expiration
)
.
\
session
.
query
(
RemoteAuthToken
)
.
filter
(
now
>
RemoteAuthToken
.
expiration
)
.
\
filter
(
RemoteAuthToken
.
token_type
!=
1
)
.
delete
()
filter
(
RemoteAuthToken
.
token_type
!=
1
)
.
delete
()
session
.
commit
()
session
.
commit
()
# Save downloaded books per user in calibre-web's own database
# Save downloaded books per user in calibre-web's own database
def
update_download
(
book_id
,
user_id
):
def
update_download
(
book_id
,
user_id
):
check
=
session
.
query
(
Downloads
)
.
filter
(
Downloads
.
user_id
==
user_id
)
.
filter
(
Downloads
.
book_id
==
check
=
session
.
query
(
Downloads
)
.
filter
(
Downloads
.
user_id
==
user_id
)
.
filter
(
Downloads
.
book_id
==
book_id
)
.
first
()
book_id
)
.
first
()
if
not
check
:
if
not
check
:
new_download
=
Downloads
(
user_id
=
user_id
,
book_id
=
book_id
)
new_download
=
Downloads
(
user_id
=
user_id
,
book_id
=
book_id
)
session
.
add
(
new_download
)
session
.
add
(
new_download
)
session
.
commit
()
session
.
commit
()
# Delete non exisiting downloaded books in calibre-web's own database
# Delete non exisiting downloaded books in calibre-web's own database
def
delete_download
(
book_id
):
def
delete_download
(
book_id
):
session
.
query
(
Downloads
)
.
filter
(
book_id
==
Downloads
.
book_id
)
.
delete
()
session
.
query
(
Downloads
)
.
filter
(
book_id
==
Downloads
.
book_id
)
.
delete
()
session
.
commit
()
session
.
commit
()
# Generate user Guest (translated text), as anoymous user, no rights
# Generate user Guest (translated text), as anoymous user, no rights
def
create_anonymous_user
(
session
):
def
create_anonymous_user
(
session
):
user
=
User
()
user
=
User
()
...
@@ -667,8 +667,12 @@ def dispose():
...
@@ -667,8 +667,12 @@ def dispose():
old_session
=
session
old_session
=
session
session
=
None
session
=
None
if
old_session
:
if
old_session
:
try
:
old_session
.
close
()
try
:
except
:
pass
old_session
.
close
()
except
Exception
:
pass
if
old_session
.
bind
:
if
old_session
.
bind
:
try
:
old_session
.
bind
.
dispose
()
try
:
except
:
pass
old_session
.
bind
.
dispose
()
except
Exception
:
pass
cps/updater.py
View file @
24c743d2
...
@@ -69,7 +69,7 @@ class Updater(threading.Thread):
...
@@ -69,7 +69,7 @@ class Updater(threading.Thread):
def
get_available_updates
(
self
,
request_method
,
locale
):
def
get_available_updates
(
self
,
request_method
,
locale
):
if
config
.
config_updatechannel
==
constants
.
UPDATE_STABLE
:
if
config
.
config_updatechannel
==
constants
.
UPDATE_STABLE
:
return
self
.
_stable_available_updates
(
request_method
)
return
self
.
_stable_available_updates
(
request_method
)
return
self
.
_nightly_available_updates
(
request_method
,
locale
)
return
self
.
_nightly_available_updates
(
request_method
,
locale
)
def
do_work
(
self
):
def
do_work
(
self
):
try
:
try
:
...
@@ -132,7 +132,7 @@ class Updater(threading.Thread):
...
@@ -132,7 +132,7 @@ class Updater(threading.Thread):
def
pause
(
self
):
def
pause
(
self
):
self
.
can_run
.
clear
()
self
.
can_run
.
clear
()
#should just resume the thread
#
should just resume the thread
def
resume
(
self
):
def
resume
(
self
):
self
.
can_run
.
set
()
self
.
can_run
.
set
()
...
@@ -268,7 +268,7 @@ class Updater(threading.Thread):
...
@@ -268,7 +268,7 @@ class Updater(threading.Thread):
def
is_venv
(
self
):
def
is_venv
(
self
):
if
(
hasattr
(
sys
,
'real_prefix'
))
or
(
hasattr
(
sys
,
'base_prefix'
)
and
sys
.
base_prefix
!=
sys
.
prefix
):
if
(
hasattr
(
sys
,
'real_prefix'
))
or
(
hasattr
(
sys
,
'base_prefix'
)
and
sys
.
base_prefix
!=
sys
.
prefix
):
return
os
.
sep
+
os
.
path
.
relpath
(
sys
.
prefix
,
constants
.
BASE_DIR
)
return
os
.
sep
+
os
.
path
.
relpath
(
sys
.
prefix
,
constants
.
BASE_DIR
)
else
:
else
:
return
False
return
False
...
@@ -280,7 +280,7 @@ class Updater(threading.Thread):
...
@@ -280,7 +280,7 @@ class Updater(threading.Thread):
@
classmethod
@
classmethod
def
_stable_version_info
(
cls
):
def
_stable_version_info
(
cls
):
return
constants
.
STABLE_VERSION
# Current version
return
constants
.
STABLE_VERSION
# Current version
def
_nightly_available_updates
(
self
,
request_method
,
locale
):
def
_nightly_available_updates
(
self
,
request_method
,
locale
):
tz
=
datetime
.
timedelta
(
seconds
=
time
.
timezone
if
(
time
.
localtime
()
.
tm_isdst
==
0
)
else
time
.
altzone
)
tz
=
datetime
.
timedelta
(
seconds
=
time
.
timezone
if
(
time
.
localtime
()
.
tm_isdst
==
0
)
else
time
.
altzone
)
...
@@ -436,7 +436,7 @@ class Updater(threading.Thread):
...
@@ -436,7 +436,7 @@ class Updater(threading.Thread):
patch_version_update
>
current_version
[
2
])
or
\
patch_version_update
>
current_version
[
2
])
or
\
minor_version_update
>
current_version
[
1
]:
minor_version_update
>
current_version
[
1
]:
parents
.
append
([
commit
[
i
][
'tag_name'
],
commit
[
i
][
'body'
]
.
replace
(
'
\r\n
'
,
'<p>'
)])
parents
.
append
([
commit
[
i
][
'tag_name'
],
commit
[
i
][
'body'
]
.
replace
(
'
\r\n
'
,
'<p>'
)])
newer
=
True
newer
=
True
i
-=
1
i
-=
1
continue
continue
if
major_version_update
<
current_version
[
0
]:
if
major_version_update
<
current_version
[
0
]:
...
...
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