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
a00d93a2
Commit
a00d93a2
authored
Feb 06, 2019
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Working again (basically)
parent
561d40f8
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
500 additions
and
452 deletions
+500
-452
cps.py
cps.py
+5
-10
__init__.py
cps/__init__.py
+83
-0
db.py
cps/db.py
+1
-1
gdriveutils.py
cps/gdriveutils.py
+1
-1
helper.py
cps/helper.py
+15
-20
pagination.py
cps/pagination.py
+75
-0
server.py
cps/server.py
+29
-43
admin.html
cps/templates/admin.html
+5
-5
book_edit.html
cps/templates/book_edit.html
+6
-6
detail.html
cps/templates/detail.html
+21
-21
index.html
cps/templates/index.html
+8
-8
layout.html
cps/templates/layout.html
+29
-29
search_form.html
cps/templates/search_form.html
+1
-1
tasks.html
cps/templates/tasks.html
+2
-2
ub.py
cps/ub.py
+10
-17
updater.py
cps/updater.py
+2
-1
web.py
cps/web.py
+181
-261
worker.py
cps/worker.py
+26
-26
No files found.
cps.py
View file @
a00d93a2
#!/usr/bin/env python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
import
os
import
sys
import
sys
from
cps
import
create_app
base_path
=
os
.
path
.
dirname
(
os
.
path
.
abspath
(
__file__
))
from
cps.web
import
web
# Insert local directories into path
from
cps
import
Server
sys
.
path
.
append
(
base_path
)
sys
.
path
.
append
(
os
.
path
.
join
(
base_path
,
'cps'
))
sys
.
path
.
append
(
os
.
path
.
join
(
base_path
,
'vendor'
))
from
cps.server
import
Server
if
__name__
==
'__main__'
:
if
__name__
==
'__main__'
:
app
=
create_app
()
app
.
register_blueprint
(
web
)
Server
.
startServer
()
Server
.
startServer
()
cps/__init__.py
View file @
a00d93a2
#!/usr/bin/env python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
# import logging
# from logging.handlers import SMTPHandler, RotatingFileHandler
# import os
from
flask
import
Flask
# , request, current_app
from
flask_login
import
LoginManager
from
flask_babel
import
Babel
# , lazy_gettext as _l
import
cache_buster
from
reverseproxy
import
ReverseProxied
import
logging
from
logging.handlers
import
RotatingFileHandler
from
flask_principal
import
Principal
# from flask_sqlalchemy import SQLAlchemy
import
os
import
ub
from
ub
import
Config
,
Settings
import
cPickle
# Normal
babel
=
Babel
()
lm
=
LoginManager
()
lm
.
login_view
=
'web.login'
lm
.
anonymous_user
=
ub
.
Anonymous
ub_session
=
ub
.
session
# ub_session.start()
config
=
Config
()
import
db
with
open
(
os
.
path
.
join
(
config
.
get_main_dir
,
'cps/translations/iso639.pickle'
),
'rb'
)
as
f
:
language_table
=
cPickle
.
load
(
f
)
searched_ids
=
{}
from
worker
import
WorkerThread
global_WorkerThread
=
WorkerThread
()
from
server
import
server
Server
=
server
()
def
create_app
():
app
=
Flask
(
__name__
)
app
.
wsgi_app
=
ReverseProxied
(
app
.
wsgi_app
)
cache_buster
.
init_cache_busting
(
app
)
formatter
=
logging
.
Formatter
(
"[
%(asctime)
s] {
%(pathname)
s:
%(lineno)
d}
%(levelname)
s -
%(message)
s"
)
try
:
file_handler
=
RotatingFileHandler
(
config
.
get_config_logfile
(),
maxBytes
=
50000
,
backupCount
=
2
)
except
IOError
:
file_handler
=
RotatingFileHandler
(
os
.
path
.
join
(
config
.
get_main_dir
,
"calibre-web.log"
),
maxBytes
=
50000
,
backupCount
=
2
)
# ToDo: reset logfile value in config class
file_handler
.
setFormatter
(
formatter
)
app
.
logger
.
addHandler
(
file_handler
)
app
.
logger
.
setLevel
(
config
.
config_log_level
)
app
.
logger
.
info
(
'Starting Calibre Web...'
)
logging
.
getLogger
(
"book_formats"
)
.
addHandler
(
file_handler
)
logging
.
getLogger
(
"book_formats"
)
.
setLevel
(
config
.
config_log_level
)
Principal
(
app
)
lm
.
init_app
(
app
)
babel
.
init_app
(
app
)
app
.
secret_key
=
os
.
getenv
(
'SECRET_KEY'
,
'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
)
Server
.
init_app
(
app
)
db
.
setup_db
()
global_WorkerThread
.
start
()
# app.config.from_object(config_class)
# db.init_app(app)
# login.init_app(app)
return
app
cps/db.py
View file @
a00d93a2
...
@@ -24,7 +24,7 @@ from sqlalchemy.orm import *
...
@@ -24,7 +24,7 @@ from sqlalchemy.orm import *
import
os
import
os
import
re
import
re
import
ast
import
ast
from
ub
import
config
from
cps
import
config
import
ub
import
ub
import
sys
import
sys
...
...
cps/gdriveutils.py
View file @
a00d93a2
...
@@ -27,7 +27,7 @@ except ImportError:
...
@@ -27,7 +27,7 @@ except ImportError:
gdrive_support
=
False
gdrive_support
=
False
import
os
import
os
from
ub
import
config
from
cps
import
config
import
cli
import
cli
import
shutil
import
shutil
from
flask
import
Response
,
stream_with_context
from
flask
import
Response
,
stream_with_context
...
...
cps/helper.py
View file @
a00d93a2
...
@@ -20,14 +20,13 @@
...
@@ -20,14 +20,13 @@
import
db
import
db
import
ub
from
cps
import
config
from
flask
import
current_app
as
app
from
flask
import
current_app
as
app
from
tempfile
import
gettempdir
from
tempfile
import
gettempdir
import
sys
import
sys
import
os
import
os
import
re
import
re
import
unicodedata
import
unicodedata
# from io import BytesIO
import
worker
import
worker
import
time
import
time
from
flask
import
send_from_directory
,
make_response
,
redirect
,
abort
from
flask
import
send_from_directory
,
make_response
,
redirect
,
abort
...
@@ -41,9 +40,10 @@ try:
...
@@ -41,9 +40,10 @@ try:
import
gdriveutils
as
gd
import
gdriveutils
as
gd
except
ImportError
:
except
ImportError
:
pass
pass
import
web
#
import web
import
random
import
random
from
subproc_wrapper
import
process_open
from
subproc_wrapper
import
process_open
import
ub
try
:
try
:
import
unidecode
import
unidecode
...
@@ -51,11 +51,6 @@ try:
...
@@ -51,11 +51,6 @@ try:
except
ImportError
:
except
ImportError
:
use_unidecode
=
False
use_unidecode
=
False
# Global variables
# updater_thread = None
global_WorkerThread
=
worker
.
WorkerThread
()
global_WorkerThread
.
start
()
def
update_download
(
book_id
,
user_id
):
def
update_download
(
book_id
,
user_id
):
check
=
ub
.
session
.
query
(
ub
.
Downloads
)
.
filter
(
ub
.
Downloads
.
user_id
==
user_id
)
.
filter
(
ub
.
Downloads
.
book_id
==
check
=
ub
.
session
.
query
(
ub
.
Downloads
)
.
filter
(
ub
.
Downloads
.
user_id
==
user_id
)
.
filter
(
ub
.
Downloads
.
book_id
==
...
@@ -73,7 +68,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
...
@@ -73,7 +68,7 @@ def convert_book_format(book_id, calibrepath, old_book_format, new_book_format,
error_message
=
_
(
u"
%(format)
s format not found for book id:
%(book)
d"
,
format
=
old_book_format
,
book
=
book_id
)
error_message
=
_
(
u"
%(format)
s format not found for book id:
%(book)
d"
,
format
=
old_book_format
,
book
=
book_id
)
app
.
logger
.
error
(
"convert_book_format: "
+
error_message
)
app
.
logger
.
error
(
"convert_book_format: "
+
error_message
)
return
error_message
return
error_message
if
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
df
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
data
.
name
+
"."
+
old_book_format
.
lower
())
df
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
data
.
name
+
"."
+
old_book_format
.
lower
())
if
df
:
if
df
:
datafile
=
os
.
path
.
join
(
calibrepath
,
book
.
path
,
data
.
name
+
u"."
+
old_book_format
.
lower
())
datafile
=
os
.
path
.
join
(
calibrepath
,
book
.
path
,
data
.
name
+
u"."
+
old_book_format
.
lower
())
...
@@ -133,7 +128,7 @@ def check_send_to_kindle(entry):
...
@@ -133,7 +128,7 @@ def check_send_to_kindle(entry):
"""
"""
if
len
(
entry
.
data
):
if
len
(
entry
.
data
):
bookformats
=
list
()
bookformats
=
list
()
if
ub
.
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
:
...
@@ -156,11 +151,11 @@ def check_send_to_kindle(entry):
...
@@ -156,11 +151,11 @@ def check_send_to_kindle(entry):
bookformats
.
append
({
'format'
:
'Azw3'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw3'
)})
bookformats
.
append
({
'format'
:
'Azw3'
,
'convert'
:
0
,
'text'
:
_
(
'Send
%(format)
s to Kindle'
,
format
=
'Azw3'
)})
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
ub
.
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'
,
'convert'
:
1
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Mobi'
)})
if
ub
.
config
.
config_ebookconverter
==
2
:
if
config
.
config_ebookconverter
==
2
:
if
'EPUB'
in
formats
and
not
'AZW3'
in
formats
:
if
'EPUB'
in
formats
and
not
'AZW3'
in
formats
:
bookformats
.
append
({
'format'
:
'Azw3'
,
'convert'
:
1
,
bookformats
.
append
({
'format'
:
'Azw3'
,
'convert'
:
1
,
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Azw3'
)})
'text'
:
_
(
'Convert
%(orig)
s to
%(format)
s and send to Kindle'
,
orig
=
'Epub'
,
format
=
'Azw3'
)})
...
@@ -407,21 +402,21 @@ def generate_random_password():
...
@@ -407,21 +402,21 @@ def generate_random_password():
################################## 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
ub
.
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
:
return
update_dir_structure_file
(
book_id
,
calibrepath
,
first_author
)
return
update_dir_structure_file
(
book_id
,
calibrepath
,
first_author
)
def
delete_book
(
book
,
calibrepath
,
book_format
):
def
delete_book
(
book
,
calibrepath
,
book_format
):
if
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
return
delete_book_gdrive
(
book
,
book_format
)
return
delete_book_gdrive
(
book
,
book_format
)
else
:
else
:
return
delete_book_file
(
book
,
calibrepath
,
book_format
)
return
delete_book_file
(
book
,
calibrepath
,
book_format
)
def
get_book_cover
(
cover_path
):
def
get_book_cover
(
cover_path
):
if
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
try
:
try
:
if
not
web
.
is_gdrive_ready
():
if
not
web
.
is_gdrive_ready
():
return
send_from_directory
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"static"
),
"generic_cover.jpg"
)
return
send_from_directory
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"static"
),
"generic_cover.jpg"
)
...
@@ -437,7 +432,7 @@ def get_book_cover(cover_path):
...
@@ -437,7 +432,7 @@ def get_book_cover(cover_path):
# traceback.print_exc()
# traceback.print_exc()
return
send_from_directory
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"static"
),
"generic_cover.jpg"
)
return
send_from_directory
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"static"
),
"generic_cover.jpg"
)
else
:
else
:
return
send_from_directory
(
os
.
path
.
join
(
ub
.
config
.
config_calibre_dir
,
cover_path
),
"cover.jpg"
)
return
send_from_directory
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
cover_path
),
"cover.jpg"
)
# saves book cover to gdrive or locally
# saves book cover to gdrive or locally
...
@@ -447,7 +442,7 @@ def save_cover(url, book_path):
...
@@ -447,7 +442,7 @@ def save_cover(url, book_path):
web
.
app
.
logger
.
error
(
"Cover is no jpg file, can't save"
)
web
.
app
.
logger
.
error
(
"Cover is no jpg file, can't save"
)
return
False
return
False
if
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
tmpDir
=
gettempdir
()
tmpDir
=
gettempdir
()
f
=
open
(
os
.
path
.
join
(
tmpDir
,
"uploaded_cover.jpg"
),
"wb"
)
f
=
open
(
os
.
path
.
join
(
tmpDir
,
"uploaded_cover.jpg"
),
"wb"
)
f
.
write
(
img
.
content
)
f
.
write
(
img
.
content
)
...
@@ -456,7 +451,7 @@ def save_cover(url, book_path):
...
@@ -456,7 +451,7 @@ def save_cover(url, book_path):
web
.
app
.
logger
.
info
(
"Cover is saved on Google Drive"
)
web
.
app
.
logger
.
info
(
"Cover is saved on Google Drive"
)
return
True
return
True
f
=
open
(
os
.
path
.
join
(
ub
.
config
.
config_calibre_dir
,
book_path
,
"cover.jpg"
),
"wb"
)
f
=
open
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book_path
,
"cover.jpg"
),
"wb"
)
f
.
write
(
img
.
content
)
f
.
write
(
img
.
content
)
f
.
close
()
f
.
close
()
web
.
app
.
logger
.
info
(
"Cover is saved"
)
web
.
app
.
logger
.
info
(
"Cover is saved"
)
...
@@ -464,7 +459,7 @@ def save_cover(url, book_path):
...
@@ -464,7 +459,7 @@ def save_cover(url, book_path):
def
do_download_file
(
book
,
book_format
,
data
,
headers
):
def
do_download_file
(
book
,
book_format
,
data
,
headers
):
if
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
startTime
=
time
.
time
()
startTime
=
time
.
time
()
df
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
data
.
name
+
"."
+
book_format
)
df
=
gd
.
getFileFromEbooksFolder
(
book
.
path
,
data
.
name
+
"."
+
book_format
)
web
.
app
.
logger
.
debug
(
time
.
time
()
-
startTime
)
web
.
app
.
logger
.
debug
(
time
.
time
()
-
startTime
)
...
@@ -473,7 +468,7 @@ def do_download_file(book, book_format, data, headers):
...
@@ -473,7 +468,7 @@ def do_download_file(book, book_format, data, headers):
else
:
else
:
abort
(
404
)
abort
(
404
)
else
:
else
:
filename
=
os
.
path
.
join
(
ub
.
config
.
config_calibre_dir
,
book
.
path
)
filename
=
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
)
if
not
os
.
path
.
isfile
(
os
.
path
.
join
(
filename
,
data
.
name
+
"."
+
book_format
)):
if
not
os
.
path
.
isfile
(
os
.
path
.
join
(
filename
,
data
.
name
+
"."
+
book_format
)):
# ToDo: improve error handling
# ToDo: improve error handling
web
.
app
.
logger
.
error
(
'File not found:
%
s'
%
os
.
path
.
join
(
filename
,
data
.
name
+
"."
+
book_format
))
web
.
app
.
logger
.
error
(
'File not found:
%
s'
%
os
.
path
.
join
(
filename
,
data
.
name
+
"."
+
book_format
))
...
...
cps/pagination.py
0 → 100644
View file @
a00d93a2
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
# Copyright (C) 2018-2019 OzzieIsaacs, cervinko, jkrehm, bodybybuddha, ok11,
# andy29485, idalin, Kyosfonica, wuqi, Kennyl, lemmsh,
# falgh1, grunjol, csitko, ytils, xybydy, trasba, vrabe,
# ruben-herold, marblepebble, JackED42, SiphonSquirrel,
# apetresc, nanu-c, mutschler
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from
math
import
ceil
# simple pagination for the feed
class
Pagination
(
object
):
def
__init__
(
self
,
page
,
per_page
,
total_count
):
self
.
page
=
int
(
page
)
self
.
per_page
=
int
(
per_page
)
self
.
total_count
=
int
(
total_count
)
@
property
def
next_offset
(
self
):
return
int
(
self
.
page
*
self
.
per_page
)
@
property
def
previous_offset
(
self
):
return
int
((
self
.
page
-
2
)
*
self
.
per_page
)
@
property
def
last_offset
(
self
):
last
=
int
(
self
.
total_count
)
-
int
(
self
.
per_page
)
if
last
<
0
:
last
=
0
return
int
(
last
)
@
property
def
pages
(
self
):
return
int
(
ceil
(
self
.
total_count
/
float
(
self
.
per_page
)))
@
property
def
has_prev
(
self
):
return
self
.
page
>
1
@
property
def
has_next
(
self
):
return
self
.
page
<
self
.
pages
# right_edge: last right_edges count of all pages are shown as number, means, if 10 pages are paginated -> 9,10 shwn
# left_edge: first left_edges count of all pages are shown as number -> 1,2 shwn
# left_current: left_current count below current page are shown as number, means if current page 5 -> 3,4 shwn
# left_current: right_current count above current page are shown as number, means if current page 5 -> 6,7 shwn
def
iter_pages
(
self
,
left_edge
=
2
,
left_current
=
2
,
right_current
=
4
,
right_edge
=
2
):
last
=
0
left_current
=
self
.
page
-
left_current
-
1
right_current
=
self
.
page
+
right_current
+
1
right_edge
=
self
.
pages
-
right_edge
for
num
in
range
(
1
,
(
self
.
pages
+
1
)):
if
num
<=
left_edge
or
(
left_current
<
num
<
right_current
)
or
num
>
right_edge
:
if
last
+
1
!=
num
:
yield
None
yield
num
last
=
num
cps/server.py
View file @
a00d93a2
...
@@ -22,7 +22,7 @@ from socket import error as SocketError
...
@@ -22,7 +22,7 @@ from socket import error as SocketError
import
sys
import
sys
import
os
import
os
import
signal
import
signal
import
web
from
cps
import
config
,
global_WorkerThread
try
:
try
:
from
gevent.pywsgi
import
WSGIServer
from
gevent.pywsgi
import
WSGIServer
...
@@ -42,82 +42,81 @@ class server:
...
@@ -42,82 +42,81 @@ class server:
wsgiserver
=
None
wsgiserver
=
None
restart
=
False
restart
=
False
app
=
None
def
__init__
(
self
):
def
__init__
(
self
):
signal
.
signal
(
signal
.
SIGINT
,
self
.
killServer
)
signal
.
signal
(
signal
.
SIGINT
,
self
.
killServer
)
signal
.
signal
(
signal
.
SIGTERM
,
self
.
killServer
)
signal
.
signal
(
signal
.
SIGTERM
,
self
.
killServer
)
def
init_app
(
self
,
application
):
self
.
app
=
application
def
start_gevent
(
self
):
def
start_gevent
(
self
):
try
:
try
:
ssl_args
=
dict
()
ssl_args
=
dict
()
certfile_path
=
web
.
ub
.
config
.
get_config_certfile
()
certfile_path
=
config
.
get_config_certfile
()
keyfile_path
=
web
.
ub
.
config
.
get_config_keyfile
()
keyfile_path
=
config
.
get_config_keyfile
()
if
certfile_path
and
keyfile_path
:
if
certfile_path
and
keyfile_path
:
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
ssl_args
=
{
"certfile"
:
certfile_path
,
ssl_args
=
{
"certfile"
:
certfile_path
,
"keyfile"
:
keyfile_path
}
"keyfile"
:
keyfile_path
}
else
:
else
:
web
.
app
.
logger
.
info
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl. Cert path:
%
s | Key path:
%
s'
%
(
certfile_path
,
keyfile_path
))
self
.
app
.
logger
.
info
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl. Cert path:
%
s | Key path:
%
s'
%
(
certfile_path
,
keyfile_path
))
if
os
.
name
==
'nt'
:
if
os
.
name
==
'nt'
:
self
.
wsgiserver
=
WSGIServer
((
'0.0.0.0'
,
web
.
ub
.
config
.
config_port
),
web
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
self
.
wsgiserver
=
WSGIServer
((
'0.0.0.0'
,
config
.
config_port
),
self
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
else
:
else
:
self
.
wsgiserver
=
WSGIServer
((
''
,
web
.
ub
.
config
.
config_port
),
web
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
self
.
wsgiserver
=
WSGIServer
((
''
,
config
.
config_port
),
self
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
web
.
py3_gevent_link
=
self
.
wsgiserver
self
.
wsgiserver
.
serve_forever
()
self
.
wsgiserver
.
serve_forever
()
except
SocketError
:
except
SocketError
:
try
:
try
:
web
.
app
.
logger
.
info
(
'Unable to listen on
\'\'
, trying on IPv4 only...'
)
self
.
app
.
logger
.
info
(
'Unable to listen on
\'\'
, trying on IPv4 only...'
)
self
.
wsgiserver
=
WSGIServer
((
'0.0.0.0'
,
web
.
ub
.
config
.
config_port
),
web
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
self
.
wsgiserver
=
WSGIServer
((
'0.0.0.0'
,
config
.
config_port
),
self
.
app
,
spawn
=
Pool
(),
**
ssl_args
)
web
.
py3_gevent_link
=
self
.
wsgiserver
self
.
wsgiserver
.
serve_forever
()
self
.
wsgiserver
.
serve_forever
()
except
(
OSError
,
SocketError
)
as
e
:
except
(
OSError
,
SocketError
)
as
e
:
web
.
app
.
logger
.
info
(
"Error starting server:
%
s"
%
e
.
strerror
)
self
.
app
.
logger
.
info
(
"Error starting server:
%
s"
%
e
.
strerror
)
print
(
"Error starting server:
%
s"
%
e
.
strerror
)
print
(
"Error starting server:
%
s"
%
e
.
strerror
)
web
.
helper
.
global_WorkerThread
.
stop
()
global_WorkerThread
.
stop
()
sys
.
exit
(
1
)
sys
.
exit
(
1
)
except
Exception
:
except
Exception
:
web
.
app
.
logger
.
info
(
"Unknown error while starting gevent"
)
self
.
app
.
logger
.
info
(
"Unknown error while starting gevent"
)
def
startServer
(
self
):
def
startServer
(
self
):
if
gevent_present
:
if
gevent_present
:
web
.
app
.
logger
.
info
(
'Starting Gevent server'
)
self
.
app
.
logger
.
info
(
'Starting Gevent server'
)
# leave subprocess out to allow forking for fetchers and processors
# leave subprocess out to allow forking for fetchers and processors
self
.
start_gevent
()
self
.
start_gevent
()
else
:
else
:
try
:
try
:
ssl
=
None
ssl
=
None
web
.
app
.
logger
.
info
(
'Starting Tornado server'
)
self
.
app
.
logger
.
info
(
'Starting Tornado server'
)
certfile_path
=
web
.
ub
.
config
.
get_config_certfile
()
certfile_path
=
config
.
get_config_certfile
()
keyfile_path
=
web
.
ub
.
config
.
get_config_keyfile
()
keyfile_path
=
config
.
get_config_keyfile
()
if
certfile_path
and
keyfile_path
:
if
certfile_path
and
keyfile_path
:
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
if
os
.
path
.
isfile
(
certfile_path
)
and
os
.
path
.
isfile
(
keyfile_path
):
ssl
=
{
"certfile"
:
certfile_path
,
ssl
=
{
"certfile"
:
certfile_path
,
"keyfile"
:
keyfile_path
}
"keyfile"
:
keyfile_path
}
else
:
else
:
web
.
app
.
logger
.
info
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl. Cert path:
%
s | Key path:
%
s'
%
(
certfile_path
,
keyfile_path
))
self
.
app
.
logger
.
info
(
'The specified paths for the ssl certificate file and/or key file seem to be broken. Ignoring ssl. Cert path:
%
s | Key path:
%
s'
%
(
certfile_path
,
keyfile_path
))
# Max Buffersize set to 200MB
# Max Buffersize set to 200MB
http_server
=
HTTPServer
(
WSGIContainer
(
web
.
app
),
http_server
=
HTTPServer
(
WSGIContainer
(
self
.
app
),
max_buffer_size
=
209700000
,
max_buffer_size
=
209700000
,
ssl_options
=
ssl
)
ssl_options
=
ssl
)
http_server
.
listen
(
web
.
ub
.
config
.
config_port
)
http_server
.
listen
(
config
.
config_port
)
self
.
wsgiserver
=
IOLoop
.
instance
()
self
.
wsgiserver
=
IOLoop
.
instance
()
self
.
wsgiserver
.
start
()
self
.
wsgiserver
.
start
()
# wait for stop signal
# wait for stop signal
self
.
wsgiserver
.
close
(
True
)
self
.
wsgiserver
.
close
(
True
)
except
SocketError
as
e
:
except
SocketError
as
e
:
web
.
app
.
logger
.
info
(
"Error starting server:
%
s"
%
e
.
strerror
)
self
.
app
.
logger
.
info
(
"Error starting server:
%
s"
%
e
.
strerror
)
print
(
"Error starting server:
%
s"
%
e
.
strerror
)
print
(
"Error starting server:
%
s"
%
e
.
strerror
)
web
.
helper
.
global_WorkerThread
.
stop
()
global_WorkerThread
.
stop
()
sys
.
exit
(
1
)
sys
.
exit
(
1
)
# ToDo: Somehow caused by circular import under python3 refactor
if
sys
.
version_info
>
(
3
,
0
):
self
.
restart
=
web
.
py3_restart_Typ
if
self
.
restart
==
True
:
if
self
.
restart
==
True
:
web
.
app
.
logger
.
info
(
"Performing restart of Calibre-Web"
)
self
.
app
.
logger
.
info
(
"Performing restart of Calibre-Web"
)
web
.
helper
.
global_WorkerThread
.
stop
()
global_WorkerThread
.
stop
()
if
os
.
name
==
'nt'
:
if
os
.
name
==
'nt'
:
arguments
=
[
"
\"
"
+
sys
.
executable
+
"
\"
"
]
arguments
=
[
"
\"
"
+
sys
.
executable
+
"
\"
"
]
for
e
in
sys
.
argv
:
for
e
in
sys
.
argv
:
...
@@ -126,26 +125,17 @@ class server:
...
@@ -126,26 +125,17 @@ class server:
else
:
else
:
os
.
execl
(
sys
.
executable
,
sys
.
executable
,
*
sys
.
argv
)
os
.
execl
(
sys
.
executable
,
sys
.
executable
,
*
sys
.
argv
)
else
:
else
:
web
.
app
.
logger
.
info
(
"Performing shutdown of Calibre-Web"
)
self
.
app
.
logger
.
info
(
"Performing shutdown of Calibre-Web"
)
web
.
helper
.
global_WorkerThread
.
stop
()
global_WorkerThread
.
stop
()
sys
.
exit
(
0
)
sys
.
exit
(
0
)
def
setRestartTyp
(
self
,
starttyp
):
def
setRestartTyp
(
self
,
starttyp
):
self
.
restart
=
starttyp
self
.
restart
=
starttyp
# ToDo: Somehow caused by circular import under python3 refactor
web
.
py3_restart_Typ
=
starttyp
def
killServer
(
self
,
signum
,
frame
):
def
killServer
(
self
,
signum
,
frame
):
self
.
stopServer
()
self
.
stopServer
()
def
stopServer
(
self
):
def
stopServer
(
self
):
# ToDo: Somehow caused by circular import under python3 refactor
if
sys
.
version_info
>
(
3
,
0
):
if
not
self
.
wsgiserver
:
if
gevent_present
:
self
.
wsgiserver
=
web
.
py3_gevent_link
else
:
self
.
wsgiserver
=
IOLoop
.
instance
()
if
self
.
wsgiserver
:
if
self
.
wsgiserver
:
if
gevent_present
:
if
gevent_present
:
self
.
wsgiserver
.
close
()
self
.
wsgiserver
.
close
()
...
@@ -155,10 +145,6 @@ class server:
...
@@ -155,10 +145,6 @@ class server:
@
staticmethod
@
staticmethod
def
getNameVersion
():
def
getNameVersion
():
if
gevent_present
:
if
gevent_present
:
return
{
'Gevent'
:
'v'
+
geventVersion
}
return
{
'Gevent'
:
'v'
+
geventVersion
}
else
:
else
:
return
{
'Tornado'
:
'v'
+
tornadoVersion
}
return
{
'Tornado'
:
'v'
+
tornadoVersion
}
# Start Instance of Server
Server
=
server
()
cps/templates/admin.html
View file @
a00d93a2
...
@@ -18,7 +18,7 @@
...
@@ -18,7 +18,7 @@
{% for user in content %}
{% for user in content %}
{% if not user.role_anonymous() or config.config_anonbrowse %}
{% if not user.role_anonymous() or config.config_anonbrowse %}
<tr>
<tr>
<td><a
href=
"{{url_for('edit_user', user_id=user.id)}}"
>
{{user.nickname}}
</a></td>
<td><a
href=
"{{url_for('
web.
edit_user', user_id=user.id)}}"
>
{{user.nickname}}
</a></td>
<td>
{{user.email}}
</td>
<td>
{{user.email}}
</td>
<td>
{{user.kindle_mail}}
</td>
<td>
{{user.kindle_mail}}
</td>
<td>
{{user.downloads.count()}}
</td>
<td>
{{user.downloads.count()}}
</td>
...
@@ -30,7 +30,7 @@
...
@@ -30,7 +30,7 @@
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
</table>
</table>
<div
class=
"btn btn-default"
id=
"admin_new_user"
><a
href=
"{{url_for('new_user')}}"
>
{{_('Add new user')}}
</a></div>
<div
class=
"btn btn-default"
id=
"admin_new_user"
><a
href=
"{{url_for('
web.
new_user')}}"
>
{{_('Add new user')}}
</a></div>
</div>
</div>
</div>
</div>
...
@@ -53,7 +53,7 @@
...
@@ -53,7 +53,7 @@
<td
class=
"hidden-xs"
>
{{email.mail_from}}
</td>
<td
class=
"hidden-xs"
>
{{email.mail_from}}
</td>
</tr>
</tr>
</table>
</table>
<div
class=
"btn btn-default"
id=
"admin_edit_email"
><a
href=
"{{url_for('edit_mailsettings')}}"
>
{{_('Change SMTP settings')}}
</a></div>
<div
class=
"btn btn-default"
id=
"admin_edit_email"
><a
href=
"{{url_for('
web.
edit_mailsettings')}}"
>
{{_('Change SMTP settings')}}
</a></div>
</div>
</div>
</div>
</div>
...
@@ -96,8 +96,8 @@
...
@@ -96,8 +96,8 @@
<div
class=
"col-xs-6 col-sm-5"
>
{% if config.config_remote_login %}
<span
class=
"glyphicon glyphicon-ok"
></span>
{% else %}
<span
class=
"glyphicon glyphicon-remove"
></span>
{% endif %}
</div>
<div
class=
"col-xs-6 col-sm-5"
>
{% if config.config_remote_login %}
<span
class=
"glyphicon glyphicon-ok"
></span>
{% else %}
<span
class=
"glyphicon glyphicon-remove"
></span>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div
class=
"btn btn-default"
><a
id=
"basic_config"
href=
"{{url_for('configuration')}}"
>
{{_('Basic Configuration')}}
</a></div>
<div
class=
"btn btn-default"
><a
id=
"basic_config"
href=
"{{url_for('
web.
configuration')}}"
>
{{_('Basic Configuration')}}
</a></div>
<div
class=
"btn btn-default"
><a
id=
"view_config"
href=
"{{url_for('view_configuration')}}"
>
{{_('UI Configuration')}}
</a></div>
<div
class=
"btn btn-default"
><a
id=
"view_config"
href=
"{{url_for('
web.
view_configuration')}}"
>
{{_('UI Configuration')}}
</a></div>
</div>
</div>
</div>
</div>
...
...
cps/templates/book_edit.html
View file @
a00d93a2
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<div
class=
"col-sm-3 col-lg-3 col-xs-12"
>
<div
class=
"col-sm-3 col-lg-3 col-xs-12"
>
<div
class=
"cover"
>
<div
class=
"cover"
>
{% if book.has_cover %}
{% if book.has_cover %}
<img
src=
"{{ url_for('get_cover', book_id=book.id) }}"
alt=
"{{ book.title }}"
/>
<img
src=
"{{ url_for('
web.
get_cover', book_id=book.id) }}"
alt=
"{{ book.title }}"
/>
{% else %}
{% else %}
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ book.title }}"
/>
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ book.title }}"
/>
{% endif %}
{% endif %}
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
<div
class=
"text-center more-stuff"
><h4>
{{_('Delete formats:')}}
</h4>
<div
class=
"text-center more-stuff"
><h4>
{{_('Delete formats:')}}
</h4>
{% for file in book.data %}
{% for file in book.data %}
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<a
href=
"{{ url_for('delete_book', book_id=book.id, book_format=file.format) }}"
class=
"btn btn-danger"
type=
"button"
>
{{_('Delete')}} - {{file.format}}
</a>
<a
href=
"{{ url_for('
web.
delete_book', book_id=book.id, book_format=file.format) }}"
class=
"btn btn-danger"
type=
"button"
>
{{_('Delete')}} - {{file.format}}
</a>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
{% if source_formats|length > 0 and conversion_formats|length > 0 %}
{% if source_formats|length > 0 and conversion_formats|length > 0 %}
<div
class=
"text-center more-stuff"
><h4>
{{_('Convert book format:')}}
</h4>
<div
class=
"text-center more-stuff"
><h4>
{{_('Convert book format:')}}
</h4>
<form
class=
"padded-bottom"
action=
"{{ url_for('convert_bookformat', book_id=book.id) }}"
method=
"post"
id=
"book_convert_frm"
>
<form
class=
"padded-bottom"
action=
"{{ url_for('
web.
convert_bookformat', book_id=book.id) }}"
method=
"post"
id=
"book_convert_frm"
>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<div
class=
"text-left"
>
<div
class=
"text-left"
>
<label
class=
"control-label"
for=
"book_format_from"
>
{{_('Convert from:')}}
</label>
<label
class=
"control-label"
for=
"book_format_from"
>
{{_('Convert from:')}}
</label>
...
@@ -53,7 +53,7 @@
...
@@ -53,7 +53,7 @@
{% endif %}
{% endif %}
</div>
</div>
<form
role=
"form"
action=
"{{ url_for('edit_book', book_id=book.id) }}"
method=
"post"
enctype=
"multipart/form-data"
id=
"book_edit_frm"
>
<form
role=
"form"
action=
"{{ url_for('
web.
edit_book', book_id=book.id) }}"
method=
"post"
enctype=
"multipart/form-data"
id=
"book_edit_frm"
>
<div
class=
"col-sm-9 col-xs-12"
>
<div
class=
"col-sm-9 col-xs-12"
>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<label
for=
"book_title"
>
{{_('Book Title')}}
</label>
<label
for=
"book_title"
>
{{_('Book Title')}}
</label>
...
@@ -175,7 +175,7 @@
...
@@ -175,7 +175,7 @@
</div>
</div>
<a
href=
"#"
id=
"get_meta"
class=
"btn btn-default"
data-toggle=
"modal"
data-target=
"#metaModal"
>
{{_('Get metadata')}}
</a>
<a
href=
"#"
id=
"get_meta"
class=
"btn btn-default"
data-toggle=
"modal"
data-target=
"#metaModal"
>
{{_('Get metadata')}}
</a>
<button
type=
"submit"
id=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
<button
type=
"submit"
id=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
<a
href=
"{{ url_for('show_book', book_id=book.id) }}"
class=
"btn btn-default"
>
{{_('Back')}}
</a>
<a
href=
"{{ url_for('
web.
show_book', book_id=book.id) }}"
class=
"btn btn-default"
>
{{_('Back')}}
</a>
</div>
</div>
</form>
</form>
...
@@ -196,7 +196,7 @@
...
@@ -196,7 +196,7 @@
</div>
</div>
<div
class=
"modal-footer"
>
<div
class=
"modal-footer"
>
<a
href=
"{{ url_for('delete_book', book_id=book.id) }}"
class=
"btn btn-danger"
>
{{_('Delete')}}
</a>
<a
href=
"{{ url_for('
web.
delete_book', book_id=book.id) }}"
class=
"btn btn-danger"
>
{{_('Delete')}}
</a>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{{_('Back')}}
</button>
<button
type=
"button"
class=
"btn btn-default"
data-dismiss=
"modal"
>
{{_('Back')}}
</button>
</div>
</div>
</div>
</div>
...
...
cps/templates/detail.html
View file @
a00d93a2
This diff is collapsed.
Click to expand it.
cps/templates/index.html
View file @
a00d93a2
...
@@ -8,21 +8,21 @@
...
@@ -8,21 +8,21 @@
{% for entry in random %}
{% for entry in random %}
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
id=
"books_rand"
>
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
id=
"books_rand"
>
<div
class=
"cover"
>
<div
class=
"cover"
>
<a
href=
"{{ url_for('show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<a
href=
"{{ url_for('
web.
show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
{% if entry.has_cover %}
{% if entry.has_cover %}
<img
src=
"{{ url_for('get_cover', book_id=entry.id) }}"
alt=
"{{ entry.title }}"
/>
<img
src=
"{{ url_for('
web.
get_cover', book_id=entry.id) }}"
alt=
"{{ entry.title }}"
/>
{% else %}
{% else %}
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ entry.title }}"
/>
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ entry.title }}"
/>
{% endif %}
{% endif %}
</a>
</a>
</div>
</div>
<div
class=
"meta"
>
<div
class=
"meta"
>
<a
href=
"{{ url_for('show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<a
href=
"{{ url_for('
web.
show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
</a>
</a>
<p
class=
"author"
>
<p
class=
"author"
>
{% for author in entry.authors %}
{% for author in entry.authors %}
<a
href=
"{{url_for('author', book_id=author.id) }}"
>
{{author.name.replace('|',',')|shortentitle(30)}}
</a>
<a
href=
"{{url_for('
web.
author', book_id=author.id) }}"
>
{{author.name.replace('|',',')|shortentitle(30)}}
</a>
{% if not loop.last %}
{% if not loop.last %}
&
&
{% endif %}
{% endif %}
...
@@ -53,21 +53,21 @@
...
@@ -53,21 +53,21 @@
{% for entry in entries %}
{% for entry in entries %}
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
id=
"books"
>
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
id=
"books"
>
<div
class=
"cover"
>
<div
class=
"cover"
>
<a
href=
"{{ url_for('show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<a
href=
"{{ url_for('
web.
show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
{% if entry.has_cover %}
{% if entry.has_cover %}
<img
src=
"{{ url_for('get_cover', book_id=entry.id) }}"
alt=
"{{ entry.title }}"
/>
<img
src=
"{{ url_for('
web.
get_cover', book_id=entry.id) }}"
alt=
"{{ entry.title }}"
/>
{% else %}
{% else %}
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ entry.title }}"
/>
<img
src=
"{{ url_for('static', filename='generic_cover.jpg') }}"
alt=
"{{ entry.title }}"
/>
{% endif %}
{% endif %}
</a>
</a>
</div>
</div>
<div
class=
"meta"
>
<div
class=
"meta"
>
<a
href=
"{{ url_for('show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<a
href=
"{{ url_for('
web.
show_book', book_id=entry.id) }}"
data-toggle=
"modal"
data-target=
"#bookDetailsModal"
data-remote=
"false"
>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
</a>
</a>
<p
class=
"author"
>
<p
class=
"author"
>
{% for author in entry.authors %}
{% for author in entry.authors %}
<a
href=
"{{url_for('author', book_id=author.id) }}"
>
{{author.name.replace('|',',')|shortentitle(30)}}
</a>
<a
href=
"{{url_for('
web.
author', book_id=author.id) }}"
>
{{author.name.replace('|',',')|shortentitle(30)}}
</a>
{% if not loop.last %}
{% if not loop.last %}
&
&
{% endif %}
{% endif %}
...
...
cps/templates/layout.html
View file @
a00d93a2
This diff is collapsed.
Click to expand it.
cps/templates/search_form.html
View file @
a00d93a2
{% extends "layout.html" %}
{% extends "layout.html" %}
{% block body %}
{% block body %}
<div
class=
"col-sm-8"
>
<div
class=
"col-sm-8"
>
<form
role=
"form"
id=
"search"
action=
"{{ url_for('advanced_search') }}"
method=
"GET"
>
<form
role=
"form"
id=
"search"
action=
"{{ url_for('
web.
advanced_search') }}"
method=
"GET"
>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<label
for=
"book_title"
>
{{_('Book Title')}}
</label>
<label
for=
"book_title"
>
{{_('Book Title')}}
</label>
<input
type=
"text"
class=
"form-control"
name=
"book_title"
id=
"book_title"
value=
""
>
<input
type=
"text"
class=
"form-control"
name=
"book_title"
id=
"book_title"
value=
""
>
...
...
cps/templates/tasks.html
View file @
a00d93a2
...
@@ -5,7 +5,7 @@
...
@@ -5,7 +5,7 @@
{% block body %}
{% block body %}
<div
class=
"discover"
>
<div
class=
"discover"
>
<h2>
{{_('Tasks list')}}
</h2>
<h2>
{{_('Tasks list')}}
</h2>
<table
class=
"table table-no-bordered"
id=
"table"
data-url=
"{{ url_for('get_email_status_json') }}"
data-sort-name=
"starttime"
data-sort-order=
"asc"
>
<table
class=
"table table-no-bordered"
id=
"table"
data-url=
"{{ url_for('
web.
get_email_status_json') }}"
data-sort-name=
"starttime"
data-sort-order=
"asc"
>
<thead>
<thead>
<tr>
<tr>
{% if g.user.role_admin() %}
{% if g.user.role_admin() %}
...
@@ -38,7 +38,7 @@
...
@@ -38,7 +38,7 @@
setInterval
(
function
()
{
setInterval
(
function
()
{
$
.
ajax
({
$
.
ajax
({
method
:
"get"
,
method
:
"get"
,
url
:
"{{ url_for('get_email_status_json')}}"
,
url
:
"{{ url_for('
web.
get_email_status_json')}}"
,
async
:
true
,
async
:
true
,
timeout
:
900
,
timeout
:
900
,
success
:
function
(
data
){
success
:
function
(
data
){
...
...
cps/ub.py
View file @
a00d93a2
...
@@ -23,7 +23,6 @@ from sqlalchemy import exc
...
@@ -23,7 +23,6 @@ from sqlalchemy import exc
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.orm
import
*
from
sqlalchemy.orm
import
*
from
flask_login
import
AnonymousUserMixin
from
flask_login
import
AnonymousUserMixin
from
flask_dance.consumer.backend.sqla
import
OAuthConsumerMixin
import
sys
import
sys
import
os
import
os
import
logging
import
logging
...
@@ -69,6 +68,10 @@ AUTO_UPDATE_STABLE = 1
...
@@ -69,6 +68,10 @@ AUTO_UPDATE_STABLE = 1
UPDATE_NIGHTLY
=
2
UPDATE_NIGHTLY
=
2
AUTO_UPDATE_NIGHTLY
=
4
AUTO_UPDATE_NIGHTLY
=
4
engine
=
create_engine
(
'sqlite:///{0}'
.
format
(
cli
.
settingspath
),
echo
=
False
)
Base
=
declarative_base
()
class
UserBase
:
class
UserBase
:
@
property
@
property
...
@@ -171,13 +174,13 @@ class UserBase:
...
@@ -171,13 +174,13 @@ class UserBase:
return
'<User
%
r>'
%
self
.
nickname
return
'<User
%
r>'
%
self
.
nickname
#Login via LDAP method
#Login via LDAP method
@
staticmethod
''''
@staticmethod
def try_login(username, password):
def try_login(username, password):
conn = get_ldap_connection()
conn = get_ldap_connection()
conn.simple_bind_s(
conn.simple_bind_s(
config.config_ldap_dn.replace("
%
s", username),
config.config_ldap_dn.replace("
%
s", username),
password
password
)
)
'''
# Baseclass for Users in Calibre-Web, settings which are depending on certain users are stored here. It is derived from
# Baseclass for Users in Calibre-Web, settings which are depending on certain users are stored here. It is derived from
# User Base (all access methods are declared there)
# User Base (all access methods are declared there)
...
@@ -197,11 +200,11 @@ class User(UserBase, Base):
...
@@ -197,11 +200,11 @@ class User(UserBase, Base):
default_language
=
Column
(
String
(
3
),
default
=
"all"
)
default_language
=
Column
(
String
(
3
),
default
=
"all"
)
mature_content
=
Column
(
Boolean
,
default
=
True
)
mature_content
=
Column
(
Boolean
,
default
=
True
)
'''
class OAuth(OAuthConsumerMixin, Base):
class OAuth(OAuthConsumerMixin, Base):
provider_user_id = Column(String(256))
provider_user_id = Column(String(256))
user_id = Column(Integer, ForeignKey(User.id))
user_id = Column(Integer, ForeignKey(User.id))
user
=
relationship
(
User
)
user = relationship(User)
'''
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
# Class for anonymous user is derived from User base and completly overrides methods and properties for the
...
@@ -815,7 +818,7 @@ def delete_download(book_id):
...
@@ -815,7 +818,7 @@ def delete_download(book_id):
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
():
def
create_anonymous_user
(
session
):
user
=
User
()
user
=
User
()
user
.
nickname
=
"Guest"
user
.
nickname
=
"Guest"
user
.
email
=
'no@email'
user
.
email
=
'no@email'
...
@@ -852,7 +855,7 @@ Session = sessionmaker()
...
@@ -852,7 +855,7 @@ Session = sessionmaker()
Session
.
configure
(
bind
=
engine
)
Session
.
configure
(
bind
=
engine
)
session
=
Session
()
session
=
Session
()
# generate database and admin and guest user, if no database is existing
if
not
os
.
path
.
exists
(
cli
.
settingspath
):
if
not
os
.
path
.
exists
(
cli
.
settingspath
):
try
:
try
:
Base
.
metadata
.
create_all
(
engine
)
Base
.
metadata
.
create_all
(
engine
)
...
@@ -865,13 +868,3 @@ else:
...
@@ -865,13 +868,3 @@ else:
Base
.
metadata
.
create_all
(
engine
)
Base
.
metadata
.
create_all
(
engine
)
migrate_Database
()
migrate_Database
()
clean_database
()
clean_database
()
#get LDAP connection
def
get_ldap_connection
():
import
ldap
conn
=
ldap
.
initialize
(
'ldap://{}'
.
format
(
config
.
config_ldap_provider_url
))
return
conn
# Generate global Settings Object accessible from every file
config
=
Config
()
searched_ids
=
{}
cps/updater.py
View file @
a00d93a2
...
@@ -28,7 +28,8 @@ from io import BytesIO
...
@@ -28,7 +28,8 @@ from io import BytesIO
import
os
import
os
import
sys
import
sys
import
shutil
import
shutil
from
ub
import
config
,
UPDATE_STABLE
from
cps
import
config
from
ub
import
UPDATE_STABLE
from
tempfile
import
gettempdir
from
tempfile
import
gettempdir
import
datetime
import
datetime
import
json
import
json
...
...
cps/web.py
View file @
a00d93a2
This diff is collapsed.
Click to expand it.
cps/worker.py
View file @
a00d93a2
...
@@ -27,11 +27,11 @@ import socket
...
@@ -27,11 +27,11 @@ import socket
import
sys
import
sys
import
os
import
os
from
email.generator
import
Generator
from
email.generator
import
Generator
import
web
from
cps
import
config
,
db
# , app
# import web
from
flask_babel
import
gettext
as
_
from
flask_babel
import
gettext
as
_
import
re
import
re
import
gdriveutils
as
gd
# import gdriveutils as gd
# import subprocess
from
subproc_wrapper
import
process_open
from
subproc_wrapper
import
process_open
...
@@ -70,8 +70,8 @@ RET_SUCCESS = 1
...
@@ -70,8 +70,8 @@ RET_SUCCESS = 1
# it in MIME Base64 encoded to
# it in MIME Base64 encoded to
def
get_attachment
(
bookpath
,
filename
):
def
get_attachment
(
bookpath
,
filename
):
"""Get file as MIMEBase message"""
"""Get file as MIMEBase message"""
calibrepath
=
web
.
config
.
config_calibre_dir
calibrepath
=
config
.
config_calibre_dir
if
web
.
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
df
=
gd
.
getFileFromEbooksFolder
(
bookpath
,
filename
)
df
=
gd
.
getFileFromEbooksFolder
(
bookpath
,
filename
)
if
df
:
if
df
:
datafile
=
os
.
path
.
join
(
calibrepath
,
bookpath
,
filename
)
datafile
=
os
.
path
.
join
(
calibrepath
,
bookpath
,
filename
)
...
@@ -90,8 +90,8 @@ def get_attachment(bookpath, filename):
...
@@ -90,8 +90,8 @@ def get_attachment(bookpath, filename):
data
=
file_
.
read
()
data
=
file_
.
read
()
file_
.
close
()
file_
.
close
()
except
IOError
as
e
:
except
IOError
as
e
:
web
.
app
.
logger
.
exception
(
e
)
# traceback.print_exc()
#
web.app.logger.exception(e) # traceback.print_exc()
web
.
app
.
logger
.
error
(
u'The requested file could not be read. Maybe wrong permissions?'
)
#
web.app.logger.error(u'The requested file could not be read. Maybe wrong permissions?')
return
None
return
None
attachment
=
MIMEBase
(
'application'
,
'octet-stream'
)
attachment
=
MIMEBase
(
'application'
,
'octet-stream'
)
...
@@ -235,7 +235,7 @@ class WorkerThread(threading.Thread):
...
@@ -235,7 +235,7 @@ class WorkerThread(threading.Thread):
curr_task
=
self
.
queue
[
self
.
current
][
'taskType'
]
curr_task
=
self
.
queue
[
self
.
current
][
'taskType'
]
filename
=
self
.
_convert_ebook_format
()
filename
=
self
.
_convert_ebook_format
()
if
filename
:
if
filename
:
if
web
.
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
gd
.
updateGdriveCalibreFromLocal
()
gd
.
updateGdriveCalibreFromLocal
()
if
curr_task
==
TASK_CONVERT
:
if
curr_task
==
TASK_CONVERT
:
self
.
add_email
(
self
.
queue
[
self
.
current
][
'settings'
][
'subject'
],
self
.
queue
[
self
.
current
][
'path'
],
self
.
add_email
(
self
.
queue
[
self
.
current
][
'settings'
][
'subject'
],
self
.
queue
[
self
.
current
][
'path'
],
...
@@ -254,8 +254,8 @@ class WorkerThread(threading.Thread):
...
@@ -254,8 +254,8 @@ class WorkerThread(threading.Thread):
# if it does - mark the conversion task as complete and return a success
# if it does - mark the conversion task as complete and return a success
# this will allow send to kindle workflow to continue to work
# this will allow send to kindle workflow to continue to work
if
os
.
path
.
isfile
(
file_path
+
format_new_ext
):
if
os
.
path
.
isfile
(
file_path
+
format_new_ext
):
web
.
app
.
logger
.
info
(
"Book id
%
d already converted to
%
s"
,
bookid
,
format_new_ext
)
#
web.app.logger.info("Book id %d already converted to %s", bookid, format_new_ext)
cur_book
=
web
.
db
.
session
.
query
(
web
.
db
.
Books
)
.
filter
(
web
.
db
.
Books
.
id
==
bookid
)
.
first
()
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
bookid
)
.
first
()
self
.
queue
[
self
.
current
][
'path'
]
=
file_path
self
.
queue
[
self
.
current
][
'path'
]
=
file_path
self
.
queue
[
self
.
current
][
'title'
]
=
cur_book
.
title
self
.
queue
[
self
.
current
][
'title'
]
=
cur_book
.
title
self
.
_handleSuccess
()
self
.
_handleSuccess
()
...
@@ -264,23 +264,23 @@ class WorkerThread(threading.Thread):
...
@@ -264,23 +264,23 @@ class WorkerThread(threading.Thread):
web
.
app
.
logger
.
info
(
"Book id
%
d - target format of
%
s does not exist. Moving forward with convert."
,
bookid
,
format_new_ext
)
web
.
app
.
logger
.
info
(
"Book id
%
d - target format of
%
s does not exist. Moving forward with convert."
,
bookid
,
format_new_ext
)
# check if converter-executable is existing
# check if converter-executable is existing
if
not
os
.
path
.
exists
(
web
.
ub
.
config
.
config_converterpath
):
if
not
os
.
path
.
exists
(
config
.
config_converterpath
):
# ToDo Text is not translated
# ToDo Text is not translated
self
.
_handleError
(
u"Convertertool
%
s not found"
%
web
.
ub
.
config
.
config_converterpath
)
self
.
_handleError
(
u"Convertertool
%
s not found"
%
config
.
config_converterpath
)
return
return
try
:
try
:
# check which converter to use kindlegen is "1"
# check which converter to use kindlegen is "1"
if
format_old_ext
==
'.epub'
and
format_new_ext
==
'.mobi'
:
if
format_old_ext
==
'.epub'
and
format_new_ext
==
'.mobi'
:
if
web
.
ub
.
config
.
config_ebookconverter
==
1
:
if
config
.
config_ebookconverter
==
1
:
'''if os.name == 'nt':
'''if os.name == 'nt':
command = web.ub.config.config_converterpath + u' "' + file_path + u'.epub"'
command = web.ub.config.config_converterpath + u' "' + file_path + u'.epub"'
if sys.version_info < (3, 0):
if sys.version_info < (3, 0):
command = command.encode(sys.getfilesystemencoding())
command = command.encode(sys.getfilesystemencoding())
else:'''
else:'''
command
=
[
web
.
ub
.
config
.
config_converterpath
,
file_path
+
u'.epub'
]
command
=
[
config
.
config_converterpath
,
file_path
+
u'.epub'
]
quotes
=
(
1
)
quotes
=
(
1
)
if
web
.
ub
.
config
.
config_ebookconverter
==
2
:
if
config
.
config_ebookconverter
==
2
:
# Linux py2.7 encode as list without quotes no empty element for parameters
# Linux py2.7 encode as list without quotes no empty element for parameters
# linux py3.x no encode and as list without quotes no empty element for parameters
# linux py3.x no encode and as list without quotes no empty element for parameters
# windows py2.7 encode as string with quotes empty element for parameters is okay
# windows py2.7 encode as string with quotes empty element for parameters is okay
...
@@ -293,11 +293,11 @@ class WorkerThread(threading.Thread):
...
@@ -293,11 +293,11 @@ class WorkerThread(threading.Thread):
if sys.version_info < (3, 0):
if sys.version_info < (3, 0):
command = command.encode(sys.getfilesystemencoding())
command = command.encode(sys.getfilesystemencoding())
else:'''
else:'''
command
=
[
web
.
ub
.
config
.
config_converterpath
,
(
file_path
+
format_old_ext
),
command
=
[
config
.
config_converterpath
,
(
file_path
+
format_old_ext
),
(
file_path
+
format_new_ext
)]
(
file_path
+
format_new_ext
)]
index
=
3
index
=
3
if
web
.
ub
.
config
.
config_calibre
:
if
config
.
config_calibre
:
parameters
=
web
.
ub
.
config
.
config_calibre
.
split
(
" "
)
parameters
=
config
.
config_calibre
.
split
(
" "
)
for
param
in
parameters
:
for
param
in
parameters
:
command
.
append
(
param
)
command
.
append
(
param
)
quotes
.
append
(
index
)
quotes
.
append
(
index
)
...
@@ -308,7 +308,7 @@ class WorkerThread(threading.Thread):
...
@@ -308,7 +308,7 @@ class WorkerThread(threading.Thread):
self
.
_handleError
(
_
(
u"Ebook-converter failed:
%(error)
s"
,
error
=
e
))
self
.
_handleError
(
_
(
u"Ebook-converter failed:
%(error)
s"
,
error
=
e
))
return
return
if
web
.
ub
.
config
.
config_ebookconverter
==
1
:
if
config
.
config_ebookconverter
==
1
:
nextline
=
p
.
communicate
()[
0
]
nextline
=
p
.
communicate
()[
0
]
# Format of error message (kindlegen translates its output texts):
# Format of error message (kindlegen translates its output texts):
# Error(prcgen):E23006: Language not recognized in metadata.The dc:Language field is mandatory.Aborting.
# Error(prcgen):E23006: Language not recognized in metadata.The dc:Language field is mandatory.Aborting.
...
@@ -336,24 +336,24 @@ class WorkerThread(threading.Thread):
...
@@ -336,24 +336,24 @@ class WorkerThread(threading.Thread):
# 0 = Info(prcgen):I1036: Mobi file built successfully
# 0 = Info(prcgen):I1036: Mobi file built successfully
# 1 = Info(prcgen):I1037: Mobi file built with WARNINGS!
# 1 = Info(prcgen):I1037: Mobi file built with WARNINGS!
# 2 = Info(prcgen):I1038: MOBI file could not be generated because of errors!
# 2 = Info(prcgen):I1038: MOBI file could not be generated because of errors!
if
(
check
<
2
and
web
.
ub
.
config
.
config_ebookconverter
==
1
)
or
\
if
(
check
<
2
and
config
.
config_ebookconverter
==
1
)
or
\
(
check
==
0
and
web
.
ub
.
config
.
config_ebookconverter
==
2
):
(
check
==
0
and
config
.
config_ebookconverter
==
2
):
cur_book
=
web
.
db
.
session
.
query
(
web
.
db
.
Books
)
.
filter
(
web
.
db
.
Books
.
id
==
bookid
)
.
first
()
cur_book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
bookid
)
.
first
()
if
os
.
path
.
isfile
(
file_path
+
format_new_ext
):
if
os
.
path
.
isfile
(
file_path
+
format_new_ext
):
new_format
=
web
.
db
.
Data
(
name
=
cur_book
.
data
[
0
]
.
name
,
new_format
=
db
.
Data
(
name
=
cur_book
.
data
[
0
]
.
name
,
book_format
=
self
.
queue
[
self
.
current
][
'settings'
][
'new_book_format'
]
.
upper
(),
book_format
=
self
.
queue
[
self
.
current
][
'settings'
][
'new_book_format'
]
.
upper
(),
book
=
bookid
,
uncompressed_size
=
os
.
path
.
getsize
(
file_path
+
format_new_ext
))
book
=
bookid
,
uncompressed_size
=
os
.
path
.
getsize
(
file_path
+
format_new_ext
))
cur_book
.
data
.
append
(
new_format
)
cur_book
.
data
.
append
(
new_format
)
web
.
db
.
session
.
commit
()
db
.
session
.
commit
()
self
.
queue
[
self
.
current
][
'path'
]
=
cur_book
.
path
self
.
queue
[
self
.
current
][
'path'
]
=
cur_book
.
path
self
.
queue
[
self
.
current
][
'title'
]
=
cur_book
.
title
self
.
queue
[
self
.
current
][
'title'
]
=
cur_book
.
title
if
web
.
ub
.
config
.
config_use_google_drive
:
if
config
.
config_use_google_drive
:
os
.
remove
(
file_path
+
format_old_ext
)
os
.
remove
(
file_path
+
format_old_ext
)
self
.
_handleSuccess
()
self
.
_handleSuccess
()
return
file_path
+
format_new_ext
return
file_path
+
format_new_ext
else
:
else
:
error_message
=
format_new_ext
.
upper
()
+
' format not found on disk'
error_message
=
format_new_ext
.
upper
()
+
' format not found on disk'
web
.
app
.
logger
.
info
(
"ebook converter failed with error while converting book"
)
#
web.app.logger.info("ebook converter failed with error while converting book")
if
not
error_message
:
if
not
error_message
:
error_message
=
'Ebook converter failed with unknown error'
error_message
=
'Ebook converter failed with unknown error'
self
.
_handleError
(
error_message
)
self
.
_handleError
(
error_message
)
...
...
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