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
ccf56302
Commit
ccf56302
authored
Apr 12, 2017
by
Radosław Kierznowski
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'refs/remotes/janeczku/master'
parents
55ee323c
24cf550b
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
778 additions
and
829 deletions
+778
-829
cps.py
cps.py
+1
-1
db.py
cps/db.py
+13
-13
epub.py
cps/epub.py
+17
-16
fb2.py
cps/fb2.py
+4
-5
gdriveutils.py
cps/gdriveutils.py
+34
-37
helper.py
cps/helper.py
+31
-24
edit_books.js
cps/static/js/edit_books.js
+101
-122
get_meta.js
cps/static/js/get_meta.js
+133
-127
main.js
cps/static/js/main.js
+66
-67
shelforder.js
cps/static/js/shelforder.js
+4
-2
book_edit.html
cps/templates/book_edit.html
+2
-2
detail.html
cps/templates/detail.html
+6
-6
discover.html
cps/templates/discover.html
+2
-2
feed.xml
cps/templates/feed.xml
+4
-4
index.html
cps/templates/index.html
+4
-4
json.txt
cps/templates/json.txt
+2
-2
list.html
cps/templates/list.html
+1
-1
search.html
cps/templates/search.html
+2
-2
shelf.html
cps/templates/shelf.html
+2
-2
user_edit.html
cps/templates/user_edit.html
+1
-1
messages.mo
cps/translations/es/LC_MESSAGES/messages.mo
+0
-0
messages.po
cps/translations/es/LC_MESSAGES/messages.po
+127
-127
messages.mo
cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo
+0
-0
messages.po
cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po
+18
-18
ub.py
cps/ub.py
+17
-49
web.py
cps/web.py
+186
-195
No files found.
cps.py
View file @
ccf56302
...
...
@@ -22,7 +22,7 @@ except ImportError:
if
__name__
==
'__main__'
:
if
web
.
ub
.
DEVELOPMENT
:
web
.
app
.
run
(
host
=
"0.0.0.0"
,
port
=
web
.
ub
.
config
.
config_port
,
debug
=
True
)
web
.
app
.
run
(
port
=
web
.
ub
.
config
.
config_port
,
debug
=
True
)
else
:
if
gevent_present
:
web
.
app
.
logger
.
info
(
'Attempting to start gevent'
)
...
...
cps/db.py
View file @
ccf56302
...
...
@@ -67,9 +67,9 @@ class Identifiers(Base):
val
=
Column
(
String
)
book
=
Column
(
Integer
,
ForeignKey
(
'books.id'
))
def
__init__
(
self
,
val
,
type
,
book
):
def
__init__
(
self
,
val
,
id_
type
,
book
):
self
.
val
=
val
self
.
type
=
type
self
.
type
=
id_
type
self
.
book
=
book
def
formatType
(
self
):
...
...
@@ -209,9 +209,9 @@ class Data(Base):
uncompressed_size
=
Column
(
Integer
)
name
=
Column
(
String
)
def
__init__
(
self
,
book
,
format
,
uncompressed_size
,
name
):
def
__init__
(
self
,
book
,
book_
format
,
uncompressed_size
,
name
):
self
.
book
=
book
self
.
format
=
format
self
.
format
=
book_
format
self
.
uncompressed_size
=
uncompressed_size
self
.
name
=
name
...
...
@@ -293,7 +293,7 @@ def setup_db():
engine
=
create_engine
(
'sqlite:///'
+
dbpath
,
echo
=
False
,
isolation_level
=
"SERIALIZABLE"
)
try
:
conn
=
engine
.
connect
()
except
Exception
as
e
:
except
Exception
:
content
=
ub
.
session
.
query
(
ub
.
Settings
)
.
first
()
content
.
config_calibre_dir
=
None
content
.
db_configured
=
False
...
...
@@ -333,15 +333,15 @@ def setup_db():
'value'
:
Column
(
String
)}
cc_classes
[
row
.
id
]
=
type
(
'Custom_Column_'
+
str
(
row
.
id
),
(
Base
,),
ccdict
)
for
id
in
cc_ids
:
if
id
[
1
]
==
'bool'
:
setattr
(
Books
,
'custom_column_'
+
str
(
id
[
0
]),
relationship
(
cc_classes
[
id
[
0
]],
for
cc_
id
in
cc_ids
:
if
cc_
id
[
1
]
==
'bool'
:
setattr
(
Books
,
'custom_column_'
+
str
(
cc_id
[
0
]),
relationship
(
cc_classes
[
cc_
id
[
0
]],
primaryjoin
=
(
Books
.
id
==
cc_classes
[
id
[
0
]]
.
book
),
Books
.
id
==
cc_classes
[
cc_
id
[
0
]]
.
book
),
backref
=
'books'
))
else
:
setattr
(
Books
,
'custom_column_'
+
str
(
id
[
0
]),
relationship
(
cc_classes
[
id
[
0
]],
secondary
=
books_custom_column_links
[
id
[
0
]],
setattr
(
Books
,
'custom_column_'
+
str
(
cc_id
[
0
]),
relationship
(
cc_classes
[
cc_
id
[
0
]],
secondary
=
books_custom_column_links
[
cc_
id
[
0
]],
backref
=
'books'
))
# Base.metadata.create_all(engine)
...
...
cps/epub.py
View file @
ccf56302
...
...
@@ -7,12 +7,13 @@ import os
import
uploader
from
iso639
import
languages
as
isoLanguages
def
extractCover
(
zip
,
coverFile
,
coverpath
,
tmp_file_name
):
def
extractCover
(
zipFile
,
coverFile
,
coverpath
,
tmp_file_name
):
if
coverFile
is
None
:
return
None
else
:
zipCoverPath
=
os
.
path
.
join
(
coverpath
,
coverFile
)
.
replace
(
'
\\
'
,
'/'
)
cf
=
zip
.
read
(
zipCoverPath
)
cf
=
zip
File
.
read
(
zipCoverPath
)
prefix
=
os
.
path
.
splitext
(
tmp_file_name
)[
0
]
tmp_cover_name
=
prefix
+
'.'
+
os
.
path
.
basename
(
zipCoverPath
)
image
=
open
(
tmp_cover_name
,
'wb'
)
...
...
@@ -28,15 +29,15 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
'dc'
:
'http://purl.org/dc/elements/1.1/'
}
z
ip
=
zipfile
.
ZipFile
(
tmp_file_path
)
epubZ
ip
=
zipfile
.
ZipFile
(
tmp_file_path
)
txt
=
z
ip
.
read
(
'META-INF/container.xml'
)
txt
=
epubZ
ip
.
read
(
'META-INF/container.xml'
)
tree
=
etree
.
fromstring
(
txt
)
cfname
=
tree
.
xpath
(
'n:rootfiles/n:rootfile/@full-path'
,
namespaces
=
ns
)[
0
]
cf
=
z
ip
.
read
(
cfname
)
cf
=
epubZ
ip
.
read
(
cfname
)
tree
=
etree
.
fromstring
(
cf
)
coverpath
=
os
.
path
.
dirname
(
cfname
)
coverpath
=
os
.
path
.
dirname
(
cfname
)
p
=
tree
.
xpath
(
'/pkg:package/pkg:metadata'
,
namespaces
=
ns
)[
0
]
...
...
@@ -57,7 +58,7 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
epub_metadata
[
'description'
]
=
""
if
epub_metadata
[
'language'
]
==
"Unknown"
:
epub_metadata
[
'language'
]
=
=
""
epub_metadata
[
'language'
]
=
""
else
:
lang
=
epub_metadata
[
'language'
]
.
split
(
'-'
,
1
)[
0
]
.
lower
()
if
len
(
lang
)
==
2
:
...
...
@@ -70,23 +71,23 @@ def get_epub_info(tmp_file_path, original_file_name, original_file_extension):
coversection
=
tree
.
xpath
(
"/pkg:package/pkg:manifest/pkg:item[@id='cover-image']/@href"
,
namespaces
=
ns
)
coverfile
=
None
if
len
(
coversection
)
>
0
:
coverfile
=
extractCover
(
z
ip
,
coversection
[
0
],
coverpath
,
tmp_file_path
)
coverfile
=
extractCover
(
epubZ
ip
,
coversection
[
0
],
coverpath
,
tmp_file_path
)
else
:
meta_cover
=
tree
.
xpath
(
"/pkg:package/pkg:metadata/pkg:meta[@name='cover']/@content"
,
namespaces
=
ns
)
if
len
(
meta_cover
)
>
0
:
coversection
=
tree
.
xpath
(
"/pkg:package/pkg:manifest/pkg:item[@id='"
+
meta_cover
[
0
]
+
"']/@href"
,
namespaces
=
ns
)
if
len
(
coversection
)
>
0
:
filetype
=
coversection
[
0
]
.
rsplit
(
'.'
,
1
)[
-
1
]
filetype
=
coversection
[
0
]
.
rsplit
(
'.'
,
1
)[
-
1
]
if
filetype
==
"xhtml"
or
filetype
==
"html"
:
#if cover is (x)html format
markup
=
zip
.
read
(
os
.
path
.
join
(
coverpath
,
coversection
[
0
]))
markup
=
epubZip
.
read
(
os
.
path
.
join
(
coverpath
,
coversection
[
0
]))
markupTree
=
etree
.
fromstring
(
markup
)
#no matter xhtml or html with no namespace
#
no matter xhtml or html with no namespace
imgsrc
=
markupTree
.
xpath
(
"//*[local-name() = 'img']/@src"
)
#imgsrc maybe startwith "../"" so fullpath join then relpath to cwd
#
imgsrc maybe startwith "../"" so fullpath join then relpath to cwd
filename
=
os
.
path
.
relpath
(
os
.
path
.
join
(
os
.
path
.
dirname
(
os
.
path
.
join
(
coverpath
,
coversection
[
0
])),
imgsrc
[
0
]))
coverfile
=
extractCover
(
z
ip
,
filename
,
""
,
tmp_file_path
)
coverfile
=
extractCover
(
epubZ
ip
,
filename
,
""
,
tmp_file_path
)
else
:
coverfile
=
extractCover
(
z
ip
,
coversection
[
0
],
coverpath
,
tmp_file_path
)
coverfile
=
extractCover
(
epubZ
ip
,
coversection
[
0
],
coverpath
,
tmp_file_path
)
if
epub_metadata
[
'title'
]
is
None
:
title
=
original_file_name
...
...
cps/fb2.py
View file @
ccf56302
...
...
@@ -2,12 +2,11 @@
# -*- coding: utf-8 -*-
from
lxml
import
etree
import
os
import
uploader
try
:
from
io
import
StringIO
except
ImportError
as
e
:
import
StringIO
#
try:
#
from io import StringIO
#except ImportError
:
#
import StringIO
def
get_fb2_info
(
tmp_file_path
,
original_file_extension
):
...
...
cps/gdriveutils.py
View file @
ccf56302
...
...
@@ -4,12 +4,11 @@ try:
from
apiclient
import
errors
except
ImportError
:
pass
import
os
,
time
import
os
from
ub
import
config
from
sqlalchemy
import
*
from
sqlalchemy
import
exc
from
sqlalchemy.ext.declarative
import
declarative_base
from
sqlalchemy.orm
import
*
...
...
@@ -165,7 +164,7 @@ def getFileFromEbooksFolder(drive, path, fileName):
if
drive
.
auth
.
access_token_expired
:
drive
.
auth
.
Refresh
()
if
path
:
sqlCheckPath
=
path
if
path
[
-
1
]
==
'/'
else
path
+
'/'
#
sqlCheckPath=path if path[-1] =='/' else path + '/'
folderId
=
getFolderId
(
path
,
drive
)
else
:
folderId
=
getEbooksFolderId
(
drive
)
...
...
@@ -195,7 +194,6 @@ def downloadFile(drive, path, filename, output):
f
.
GetContentFile
(
output
)
def
backupCalibreDbAndOptionalDownload
(
drive
,
f
=
None
):
pass
if
not
drive
:
drive
=
getDrive
()
if
drive
.
auth
.
access_token_expired
:
...
...
@@ -209,6 +207,7 @@ def backupCalibreDbAndOptionalDownload(drive, f=None):
if
f
:
databaseFile
.
GetContentFile
(
f
)
def
copyToDrive
(
drive
,
uploadFile
,
createRoot
,
replaceFiles
,
ignoreFiles
=
[],
parent
=
None
,
prevDir
=
''
):
...
...
@@ -273,20 +272,19 @@ def watchChange(drive, channel_id, channel_type, channel_address,
drive
=
getDrive
()
if
drive
.
auth
.
access_token_expired
:
drive
.
auth
.
Refresh
()
"""Watch for all changes to a user's Drive.
Args:
service: Drive API service instance.
channel_id: Unique string that identifies this channel.
channel_type: Type of delivery mechanism used for this channel.
channel_address: Address where notifications are delivered.
channel_token: An arbitrary string delivered to the target address with
each notification delivered over this channel. Optional.
channel_address: Address where notifications are delivered. Optional.
Returns:
The created channel if successful
Raises:
apiclient.errors.HttpError: if http request to create channel fails.
"""
# Watch for all changes to a user's Drive.
# Args:
# service: Drive API service instance.
# channel_id: Unique string that identifies this channel.
# channel_type: Type of delivery mechanism used for this channel.
# channel_address: Address where notifications are delivered.
# channel_token: An arbitrary string delivered to the target address with
# each notification delivered over this channel. Optional.
# channel_address: Address where notifications are delivered. Optional.
# Returns:
# The created channel if successful
# Raises:
# apiclient.errors.HttpError: if http request to create channel fails.
body
=
{
'id'
:
channel_id
,
'type'
:
channel_type
,
...
...
@@ -344,7 +342,7 @@ def stopChannel(drive, channel_id, resource_id):
drive
=
getDrive
()
if
drive
.
auth
.
access_token_expired
:
drive
.
auth
.
Refresh
()
service
=
drive
.
auth
.
service
#
service=drive.auth.service
body
=
{
'id'
:
channel_id
,
'resourceId'
:
resource_id
...
...
@@ -356,15 +354,14 @@ def getChangeById (drive, change_id):
drive
=
getDrive
()
if
drive
.
auth
.
access_token_expired
:
drive
.
auth
.
Refresh
()
"""Print a single Change resource information.
Args:
service: Drive API service instance.
change_id: ID of the Change resource to retrieve.
"""
# Print a single Change resource information.
#
# Args:
# service: Drive API service instance.
# change_id: ID of the Change resource to retrieve.
try
:
change
=
drive
.
auth
.
service
.
changes
()
.
get
(
changeId
=
change_id
)
.
execute
()
return
change
except
errors
.
HttpError
,
error
:
except
(
errors
.
HttpError
,
error
)
:
web
.
app
.
logger
.
exception
(
error
)
return
None
cps/helper.py
View file @
ccf56302
...
...
@@ -13,6 +13,7 @@ import os
import
traceback
import
re
import
unicodedata
try
:
from
StringIO
import
StringIO
from
email.MIMEBase
import
MIMEBase
...
...
@@ -43,9 +44,9 @@ import web
try
:
import
unidecode
use_unidecode
=
True
use_unidecode
=
True
except
Exception
as
e
:
use_unidecode
=
False
use_unidecode
=
False
# Global variables
global_task
=
None
...
...
@@ -80,7 +81,7 @@ def make_mobi(book_id, calibrepath):
file_path
=
os
.
path
.
join
(
calibrepath
,
book
.
path
,
data
.
name
)
if
os
.
path
.
exists
(
file_path
+
u".epub"
):
p
=
subprocess
.
Popen
((
kindlegen
+
"
\"
"
+
file_path
+
u".epub
\"
"
)
.
encode
(
sys
.
getfilesystemencoding
()),
s
hell
=
True
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
stdin
=
subprocess
.
PIPE
)
s
tdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
# Poll process for new output until finished
while
True
:
nextline
=
p
.
stdout
.
readline
()
...
...
@@ -93,7 +94,7 @@ def make_mobi(book_id, calibrepath):
if
not
check
or
check
<
2
:
book
.
data
.
append
(
db
.
Data
(
name
=
book
.
data
[
0
]
.
name
,
format
=
"MOBI"
,
book_
format
=
"MOBI"
,
book
=
book
.
id
,
uncompressed_size
=
os
.
path
.
getsize
(
file_path
+
".mobi"
)
))
...
...
@@ -242,7 +243,7 @@ def get_valid_filename(value, replace_whitespace=True):
Returns the given string converted to a string that can be used for a clean
filename. Limits num characters to 128 max.
"""
if
value
[
-
1
:]
==
u'.'
:
if
value
[
-
1
:]
==
u'.'
:
value
=
value
[:
-
1
]
+
u'_'
if
use_unidecode
:
value
=
(
unidecode
.
unidecode
(
value
))
.
strip
()
...
...
@@ -251,7 +252,7 @@ def get_valid_filename(value, replace_whitespace=True):
value
=
value
.
replace
(
u'ß'
,
u'ss'
)
value
=
unicodedata
.
normalize
(
'NFKD'
,
value
)
re_slugify
=
re
.
compile
(
'[
\
W
\
s-]'
,
re
.
UNICODE
)
if
type
(
value
)
is
str
:
#Python3 str, Python2 unicode
if
isinstance
(
value
,
str
)
:
#Python3 str, Python2 unicode
value
=
re_slugify
.
sub
(
''
,
value
)
.
strip
()
else
:
value
=
unicode
(
re_slugify
.
sub
(
''
,
value
)
.
strip
())
...
...
@@ -266,7 +267,7 @@ def get_sorted_author(value):
regexes
=
[
"^(JR|SR)
\
.?$"
,
"^I{1,3}
\
.?$"
,
"^IV
\
.?$"
]
combined
=
"("
+
")|("
.
join
(
regexes
)
+
")"
value
=
value
.
split
(
" "
)
if
re
.
match
(
combined
,
value
[
-
1
]
.
upper
()):
if
re
.
match
(
combined
,
value
[
-
1
]
.
upper
()):
value2
=
value
[
-
2
]
+
", "
+
" "
.
join
(
value
[:
-
2
])
+
" "
+
value
[
-
1
]
else
:
value2
=
value
[
-
1
]
+
", "
+
" "
.
join
(
value
[:
-
1
])
...
...
@@ -295,6 +296,7 @@ def update_dir_stucture(book_id, calibrepath):
book
.
path
=
new_authordir
+
'/'
+
book
.
path
.
split
(
'/'
)[
1
]
db
.
session
.
commit
()
def
update_dir_structure_gdrive
(
book_id
):
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"title_sort"
,
1
,
db
.
title_sort
)
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
...
...
@@ -313,7 +315,7 @@ def update_dir_structure_gdrive(book_id):
if
authordir
!=
new_authordir
:
gFile
=
gd
.
getFileFromEbooksFolder
(
web
.
Gdrive
.
Instance
()
.
drive
,
None
,
authordir
)
gFile
[
'title'
]
=
new_authordir
gFile
[
'title'
]
=
new_authordir
gFile
.
Upload
()
book
.
path
=
new_authordir
+
'/'
+
book
.
path
.
split
(
'/'
)[
1
]
...
...
@@ -327,23 +329,23 @@ class Updater(threading.Thread):
def
run
(
self
):
global
global_task
self
.
status
=
1
self
.
status
=
1
r
=
requests
.
get
(
'https://api.github.com/repos/janeczku/calibre-web/zipball/master'
,
stream
=
True
)
fname
=
re
.
findall
(
"filename=(.+)"
,
r
.
headers
[
'content-disposition'
])[
0
]
self
.
status
=
2
self
.
status
=
2
z
=
zipfile
.
ZipFile
(
StringIO
(
r
.
content
))
self
.
status
=
3
self
.
status
=
3
tmp_dir
=
gettempdir
()
z
.
extractall
(
tmp_dir
)
self
.
status
=
4
self
.
status
=
4
self
.
update_source
(
os
.
path
.
join
(
tmp_dir
,
os
.
path
.
splitext
(
fname
)[
0
]),
ub
.
config
.
get_main_dir
)
self
.
status
=
5
self
.
status
=
5
global_task
=
0
db
.
session
.
close
()
db
.
engine
.
dispose
()
ub
.
session
.
close
()
ub
.
engine
.
dispose
()
self
.
status
=
6
self
.
status
=
6
if
web
.
gevent_server
:
web
.
gevent_server
.
stop
()
...
...
@@ -351,17 +353,20 @@ class Updater(threading.Thread):
# stop tornado server
server
=
IOLoop
.
instance
()
server
.
add_callback
(
server
.
stop
)
self
.
status
=
7
self
.
status
=
7
def
get_update_status
(
self
):
return
self
.
status
@
classmethod
def
file_to_list
(
self
,
file
):
return
[
x
.
strip
()
for
x
in
open
(
file
,
'r'
)
if
not
x
.
startswith
(
'#EXT'
)]
@
classmethod
def
one_minus_two
(
self
,
one
,
two
):
return
[
x
for
x
in
one
if
x
not
in
set
(
two
)]
@
classmethod
def
reduce_dirs
(
self
,
delete_files
,
new_list
):
new_delete
=
[]
for
file
in
delete_files
:
...
...
@@ -382,6 +387,7 @@ class Updater(threading.Thread):
break
return
list
(
set
(
new_delete
))
@
classmethod
def
reduce_files
(
self
,
remove_items
,
exclude_items
):
rf
=
[]
for
item
in
remove_items
:
...
...
@@ -389,6 +395,7 @@ class Updater(threading.Thread):
rf
.
append
(
item
)
return
rf
@
classmethod
def
moveallfiles
(
self
,
root_src_dir
,
root_dst_dir
):
change_permissions
=
True
if
sys
.
platform
==
"win32"
or
sys
.
platform
==
"darwin"
:
...
...
@@ -432,7 +439,7 @@ class Updater(threading.Thread):
old_list
=
list
()
exclude
=
(
'vendor'
+
os
.
sep
+
'kindlegen.exe'
,
'vendor'
+
os
.
sep
+
'kindlegen'
,
os
.
sep
+
'app.db'
,
os
.
sep
+
'vendor'
,
os
.
sep
+
'calibre-web.log'
)
os
.
sep
+
'vendor'
,
os
.
sep
+
'calibre-web.log'
)
for
root
,
dirs
,
files
in
os
.
walk
(
destination
,
topdown
=
True
):
for
name
in
files
:
old_list
.
append
(
os
.
path
.
join
(
root
,
name
)
.
replace
(
destination
,
''
))
...
...
@@ -462,9 +469,9 @@ class Updater(threading.Thread):
else
:
try
:
logging
.
getLogger
(
'cps.web'
)
.
debug
(
"Delete file "
+
item_path
)
log_from_thread
(
"Delete file "
+
item_path
)
#
log_from_thread("Delete file " + item_path)
os
.
remove
(
item_path
)
except
Exception
as
e
:
except
Exception
:
logging
.
getLogger
(
'cps.web'
)
.
debug
(
"Could not remove:"
+
item_path
)
shutil
.
rmtree
(
source
,
ignore_errors
=
True
)
cps/static/js/edit_books.js
View file @
ccf56302
/**
* Created by SpeedProg on 05.04.2015.
*/
/* global Bloodhound */
/*
Takes a prefix, query typeahead callback, Bloodhound typeahead adapter
and returns the completions it gets from the bloodhound engine prefixed.
*/
function
prefixed
_source
(
prefix
,
query
,
cb
,
bh_a
dapter
)
{
bh
_a
dapter
(
query
,
function
(
retArray
){
function
prefixed
Source
(
prefix
,
query
,
cb
,
bhA
dapter
)
{
bh
A
dapter
(
query
,
function
(
retArray
){
var
matches
=
[];
for
(
var
i
=
0
;
i
<
retArray
.
length
;
i
++
)
{
var
obj
=
{
name
:
prefix
+
retArray
[
i
].
name
};
...
...
@@ -16,184 +18,161 @@ function prefixed_source(prefix, query, cb, bh_adapter) {
cb
(
matches
);
});
}
function
get
_p
ath
(){
var
jsFileLocation
=
$
(
'script[src*=edit_books]'
).
attr
(
'src'
);
// the js file path
jsFileLocation
=
jsFileLocation
.
replace
(
'/static/js/edit_books.js'
,
''
);
// the js folder path
function
get
P
ath
(){
var
jsFileLocation
=
$
(
"script[src*=edit_books]"
).
attr
(
"src"
);
// the js file path
jsFileLocation
=
jsFileLocation
.
replace
(
"/static/js/edit_books.js"
,
""
);
// the js folder path
return
jsFileLocation
;
}
var
authors
=
new
Bloodhound
({
name
:
'authors'
,
datumTokenizer
:
function
(
datum
)
{
name
:
"authors"
,
datumTokenizer
(
datum
)
{
return
[
datum
.
name
];
},
queryTokenizer
:
Bloodhound
.
tokenizers
.
whitespace
,
remote
:
{
url
:
get_path
()
+
'/get_authors_json?q=%QUERY'
}
});
function
authors_source
(
query
,
cb
)
{
var
bh_adapter
=
authors
.
ttAdapter
();
var
tokens
=
query
.
split
(
"&"
);
var
current_author
=
tokens
[
tokens
.
length
-
1
].
trim
();
tokens
.
splice
(
tokens
.
length
-
1
,
1
);
// remove last element
var
prefix
=
""
;
for
(
var
i
=
0
;
i
<
tokens
.
length
;
i
++
)
{
var
author
=
tokens
[
i
].
trim
();
prefix
+=
author
+
" & "
;
}
prefixed_source
(
prefix
,
current_author
,
cb
,
bh_adapter
);
}
var
promise
=
authors
.
initialize
();
promise
.
done
(
function
(){
$
(
"#bookAuthor"
).
typeahead
(
{
highlight
:
true
,
minLength
:
1
,
hint
:
true
},
{
name
:
'authors'
,
displayKey
:
'name'
,
source
:
authors_source
url
:
getPath
()
+
"/get_authors_json?q=%QUERY"
}
)
});
var
series
=
new
Bloodhound
({
name
:
'series'
,
datumTokenizer
:
function
(
datum
)
{
name
:
"series"
,
datumTokenizer
(
datum
)
{
return
[
datum
.
name
];
},
queryTokenizer
:
function
(
query
)
{
queryTokenizer
(
query
)
{
return
[
query
];
},
remote
:
{
url
:
get_path
()
+
'/get_series_json?q='
,
replace
:
function
(
url
,
query
)
{
url_query
=
url
+
encodeURIComponent
(
query
);
return
url_query
;
url
:
getPath
()
+
"/get_series_json?q="
,
replace
(
url
,
query
)
{
return
url
+
encodeURIComponent
(
query
);
}
}
});
var
promise
=
series
.
initialize
();
promise
.
done
(
function
(){
$
(
"#series"
).
typeahead
(
{
highlight
:
true
,
minLength
:
0
,
hint
:
true
},
{
name
:
'series'
,
displayKey
:
'name'
,
source
:
series
.
ttAdapter
()
}
)
});
var
tags
=
new
Bloodhound
({
name
:
'tags'
,
datumTokenizer
:
function
(
datum
)
{
name
:
"tags"
,
datumTokenizer
(
datum
)
{
return
[
datum
.
name
];
},
queryTokenizer
:
function
(
query
)
{
tokens
=
query
.
split
(
","
);
queryTokenizer
(
query
)
{
var
tokens
=
query
.
split
(
","
);
tokens
=
[
tokens
[
tokens
.
length
-
1
].
trim
()];
return
tokens
return
tokens
;
},
remote
:
{
url
:
get
_path
()
+
'/get_tags_json?q=%QUERY'
url
:
get
Path
()
+
"/get_tags_json?q=%QUERY"
}
});
function
tag_source
(
query
,
cb
)
{
var
bh_adapter
=
tags
.
ttAdapter
();
var
languages
=
new
Bloodhound
({
name
:
"languages"
,
datumTokenizer
(
datum
)
{
return
[
datum
.
name
];
},
queryTokenizer
(
query
)
{
return
[
query
];
},
remote
:
{
url
:
getPath
()
+
"/get_languages_json?q="
,
replace
(
url
,
query
)
{
return
url
+
encodeURIComponent
(
query
);
}
}
});
var
tokens
=
query
.
split
(
","
);
var
current_tag
=
tokens
[
tokens
.
length
-
1
].
trim
();
function
sourceSplit
(
query
,
cb
,
split
,
source
)
{
var
bhAdapter
=
source
.
ttAdapter
();
var
tokens
=
query
.
split
(
split
);
var
currentSource
=
tokens
[
tokens
.
length
-
1
].
trim
();
tokens
.
splice
(
tokens
.
length
-
1
,
1
);
// remove last element
var
prefix
=
""
;
var
newSplit
;
if
(
split
===
"&"
){
newSplit
=
" "
+
split
+
" "
;
}
else
{
newSplit
=
split
+
" "
;
}
for
(
var
i
=
0
;
i
<
tokens
.
length
;
i
++
)
{
var
tag
=
tokens
[
i
].
trim
();
prefix
+=
tag
+
", "
;
prefix
+=
tokens
[
i
].
trim
()
+
newSplit
;
}
prefixed_source
(
prefix
,
current_tag
,
cb
,
bh_adapter
);
prefixedSource
(
prefix
,
currentSource
,
cb
,
bhAdapter
);
}
var
promise
=
tag
s
.
initialize
();
promise
.
done
(
function
(){
$
(
"#
tags
"
).
typeahead
(
var
promise
Authors
=
author
s
.
initialize
();
promise
Authors
.
done
(
function
(){
$
(
"#
bookAuthor
"
).
typeahead
(
{
highlight
:
true
,
minLength
:
0
,
highlight
:
true
,
minLength
:
1
,
hint
:
true
},
{
name
:
'tags'
,
displayKey
:
'name'
,
source
:
tag_source
name
:
"authors"
,
displayKey
:
"name"
,
source
(
query
,
cb
){
return
sourceSplit
(
query
,
cb
,
"&"
,
authors
);
//sourceSplit //("&")
}
)
});
});
var
languages
=
new
Bloodhound
({
name
:
'languages'
,
datumTokenizer
:
function
(
datum
)
{
return
[
datum
.
name
];
},
queryTokenizer
:
function
(
query
)
{
return
[
query
];
},
remote
:
{
url
:
get_path
()
+
'/get_languages_json?q='
,
replace
:
function
(
url
,
query
)
{
url_query
=
url
+
encodeURIComponent
(
query
);
return
url_query
;
}
var
promiseSeries
=
series
.
initialize
();
promiseSeries
.
done
(
function
(){
$
(
"#series"
).
typeahead
(
{
highlight
:
true
,
minLength
:
0
,
hint
:
true
},
{
name
:
"series"
,
displayKey
:
"name"
,
source
:
series
.
ttAdapter
()
}
);
});
function
language_source
(
query
,
cb
)
{
var
bh_adapter
=
languages
.
ttAdapter
();
var
tokens
=
query
.
split
(
","
);
var
current_language
=
tokens
[
tokens
.
length
-
1
].
trim
();
tokens
.
splice
(
tokens
.
length
-
1
,
1
);
// remove last element
var
prefix
=
""
;
for
(
var
i
=
0
;
i
<
tokens
.
length
;
i
++
)
{
var
tag
=
tokens
[
i
].
trim
();
prefix
+=
tag
+
", "
;
var
promiseTags
=
tags
.
initialize
();
promiseTags
.
done
(
function
(){
$
(
"#tags"
).
typeahead
(
{
highlight
:
true
,
minLength
:
0
,
hint
:
true
},
{
name
:
"tags"
,
displayKey
:
"name"
,
source
(
query
,
cb
){
return
sourceSplit
(
query
,
cb
,
","
,
tags
)
;
}
});
});
prefixed_source
(
prefix
,
current_language
,
cb
,
bh_adapter
);
}
var
promise
=
languages
.
initialize
();
promise
.
done
(
function
(){
var
promiseLanguages
=
languages
.
initialize
();
promiseLanguages
.
done
(
function
(){
$
(
"#languages"
).
typeahead
(
{
highlight
:
true
,
minLength
:
0
,
hint
:
true
},
{
name
:
'languages'
,
displayKey
:
'name'
,
source
:
language_source
name
:
"languages"
,
displayKey
:
"name"
,
source
(
query
,
cb
){
return
sourceSplit
(
query
,
cb
,
","
,
languages
);
//(",")
}
)
});
});
});
$
(
'form'
).
on
(
'change input typeahead:selected'
,
function
(
data
){
form
=
$
(
'form'
).
serialize
();
$
.
getJSON
(
get
_p
ath
()
+
"/get_matching_tags"
,
form
,
function
(
data
)
{
$
(
'.tags_click'
).
each
(
function
()
{
if
(
$
.
inArray
(
parseInt
(
$
(
this
).
children
(
'input'
).
first
().
val
(),
10
),
data
.
tags
)
==
-
1
)
{
if
(
!
(
$
(
this
).
hasClass
(
'active'
)))
{
$
(
this
).
addClass
(
'disabled'
);
$
(
"form"
).
on
(
"change input typeahead:selected"
,
function
(
data
){
var
form
=
$
(
"form"
).
serialize
();
$
.
getJSON
(
get
P
ath
()
+
"/get_matching_tags"
,
form
,
function
(
data
)
{
$
(
".tags_click"
).
each
(
function
()
{
if
(
$
.
inArray
(
parseInt
(
$
(
this
).
children
(
"input"
).
first
().
val
(),
10
),
data
.
tags
)
=
==
-
1
)
{
if
(
!
(
$
(
this
).
hasClass
(
"active"
)))
{
$
(
this
).
addClass
(
"disabled"
);
}
}
else
{
$
(
this
).
removeClass
(
'disabled'
);
$
(
this
).
removeClass
(
"disabled"
);
}
});
});
...
...
cps/static/js/get_meta.js
View file @
ccf56302
...
...
@@ -4,176 +4,182 @@
* Google Books api document: https://developers.google.com/books/docs/v1/using
* Douban Books api document: https://developers.douban.com/wiki/?title=book_v2 (Chinese Only)
*/
/* global i18nMsg */
$
(
document
).
ready
(
function
()
{
var
msg
=
i18n
_m
sg
;
var
douban
=
'https://api.douban.com'
;
var
db
_search
=
'/v2/book/search'
;
var
db_get_info
=
'/v2/book/'
;
var
db_get_info_by_isbn
=
'/v2/book/isbn/ '
;
var
db
_d
one
=
false
;
var
msg
=
i18n
M
sg
;
var
douban
=
"https://api.douban.com"
;
var
db
Search
=
"/v2/book/search"
;
// var dbGetInfo = "/v2/book/"
;
// var db_get_info_by_isbn = "/v2/book/isbn/ "
;
var
db
D
one
=
false
;
var
google
=
'https://www.googleapis.com/'
;
var
gg
_search
=
'/books/v1/volumes'
;
var
gg_get_info
=
'/books/v1/volumes/'
;
var
gg
_d
one
=
false
;
var
google
=
"https://www.googleapis.com/"
;
var
gg
Search
=
"/books/v1/volumes"
;
// var gg_get_info = "/books/v1/volumes/"
;
var
gg
D
one
=
false
;
var
db_results
=
[];
var
gg_results
=
[];
var
show_flag
=
0
;
String
.
prototype
.
replaceAll
=
function
(
s1
,
s2
)
{
return
this
.
replace
(
new
RegExp
(
s1
,
"gm"
),
s2
);
var
dbResults
=
[];
var
ggResults
=
[];
var
showFlag
=
0
;
String
.
prototype
.
replaceAll
=
function
(
s1
,
s2
)
{
return
this
.
replace
(
new
RegExp
(
s1
,
"gm"
),
s2
);
};
function
showResult
()
{
var
book
;
var
i
;
var
bookHtml
;
showFlag
++
;
if
(
showFlag
===
1
)
{
$
(
"#meta-info"
).
html
(
"<ul id=
\"
book-list
\"
class=
\"
media-list
\"
></ul>"
);
}
if
(
ggDone
&&
dbDone
)
{
if
(
!
ggResults
&&
!
dbResults
)
{
$
(
"#meta-info"
).
html
(
"<p class=
\"
text-danger
\"
>"
+
msg
.
no_result
+
"</p>"
);
return
;
}
}
if
(
ggDone
&&
ggResults
.
length
>
0
)
{
for
(
i
=
0
;
i
<
ggResults
.
length
;
i
++
)
{
book
=
ggResults
[
i
];
var
bookCover
;
if
(
book
.
volumeInfo
.
imageLinks
)
{
bookCover
=
book
.
volumeInfo
.
imageLinks
.
thumbnail
;
}
else
{
bookCover
=
"/static/generic_cover.jpg"
;
}
bookHtml
=
"<li class=
\"
media
\"
>"
+
"<img class=
\"
pull-left img-responsive
\"
data-toggle=
\"
modal
\"
data-target=
\"
#metaModal
\"
src=
\"
"
+
bookCover
+
"
\"
alt=
\"
Cover
\"
style=
\"
width:100px;height:150px
\"
onclick='javascript:getMeta(
\"
google
\"
,"
+
i
+
")
\\
>
\"
"
+
"<div class=
\"
media-body
\"
>"
+
"<h4 class=
\"
media-heading
\"
><a href=
\"
https://books.google.com/books?id="
+
book
.
id
+
"
\"
target=
\"
_blank
\"
>"
+
book
.
volumeInfo
.
title
+
"</a></h4>"
+
"<p>"
+
msg
.
author
+
":"
+
book
.
volumeInfo
.
authors
+
"</p>"
+
"<p>"
+
msg
.
publisher
+
":"
+
book
.
volumeInfo
.
publisher
+
"</p>"
+
"<p>"
+
msg
.
description
+
":"
+
book
.
volumeInfo
.
description
+
"</p>"
+
"<p>"
+
msg
.
source
+
":<a href=
\"
https://books.google.com
\"
target=
\"
_blank
\"
>Google Books</a></p>"
+
"</div>"
+
"</li>"
;
$
(
"#book-list"
).
append
(
bookHtml
);
}
ggDone
=
false
;
}
if
(
dbDone
&&
dbResults
.
length
>
0
)
{
for
(
i
=
0
;
i
<
dbResults
.
length
;
i
++
)
{
book
=
dbResults
[
i
];
bookHtml
=
"<li class=
\"
media
\"
>"
+
"<img class=
\"
pull-left img-responsive
\"
data-toggle=
\"
modal
\"
data-target=
\"
#metaModal
\"
src=
\"
"
+
book
.
image
+
"
\"
alt=
\"
Cover
\"
style=
\"
width:100px;height: 150px
\"
onclick='javascript:getMeta(
\"
douban
\"
,"
+
i
+
")
\\
'>"
+
"<div class=
\"
media-body
\"
>"
+
"<h4 class=
\"
media-heading
\"
><a href=
\"
https://book.douban.com/subject/"
+
book
.
id
+
"
\"
target=
\"
_blank
\"
>"
+
book
.
title
+
"</a></h4>"
+
"<p>"
+
msg
.
author
+
":"
+
book
.
author
+
"</p>"
+
"<p>"
+
msg
.
publisher
+
":"
+
book
.
publisher
+
"</p>"
+
"<p>"
+
msg
.
description
+
":"
+
book
.
summary
+
"</p>"
+
"<p>"
+
msg
.
source
+
":<a href=
\"
https://book.douban.com
\"
target=
\"
_blank
\"
>Douban Books</a></p>"
+
"</div>"
+
"</li>"
;
$
(
"#book-list"
).
append
(
bookHtml
);
}
dbDone
=
false
;
}
}
gg_search_book
=
function
(
title
)
{
title
=
title
.
replaceAll
(
/
\s
+/
,
'+'
);
var
url
=
google
+
gg
_search
+
'?q='
+
title
;
function
ggSearchBook
(
title
)
{
title
=
title
.
replaceAll
(
/
\s
+/
,
"+"
);
var
url
=
google
+
gg
Search
+
"?q="
+
title
;
$
.
ajax
({
url
:
url
,
url
,
type
:
"GET"
,
dataType
:
"jsonp"
,
jsonp
:
'callback'
,
success
:
function
(
data
)
{
gg
_r
esults
=
data
.
items
;
jsonp
:
"callback"
,
success
(
data
)
{
gg
R
esults
=
data
.
items
;
},
complete
:
function
()
{
gg
_d
one
=
true
;
show
_r
esult
();
complete
()
{
gg
D
one
=
true
;
show
R
esult
();
}
});
}
get_meta
=
function
(
source
,
id
)
{
function
getMeta
(
source
,
id
)
{
var
meta
;
if
(
source
==
'google'
)
{;
meta
=
gg_results
[
id
];
$
(
'#description'
).
val
(
meta
.
volumeInfo
.
description
);
$
(
'#bookAuthor'
).
val
(
meta
.
volumeInfo
.
authors
.
join
(
' & '
));
$
(
'#book_title'
).
val
(
meta
.
volumeInfo
.
title
);
var
tags
;
if
(
source
===
"google"
)
{
meta
=
ggResults
[
id
];
$
(
"#description"
).
val
(
meta
.
volumeInfo
.
description
);
$
(
"#bookAuthor"
).
val
(
meta
.
volumeInfo
.
authors
.
join
(
" & "
));
$
(
"#book_title"
).
val
(
meta
.
volumeInfo
.
title
);
if
(
meta
.
volumeInfo
.
categories
)
{
var
tags
=
meta
.
volumeInfo
.
categories
.
join
(
','
);
$
(
'#tags'
).
val
(
tags
);
tags
=
meta
.
volumeInfo
.
categories
.
join
(
","
);
$
(
"#tags"
).
val
(
tags
);
}
if
(
meta
.
volumeInfo
.
averageRating
)
{
$
(
'#rating'
).
val
(
Math
.
round
(
meta
.
volumeInfo
.
averageRating
));
$
(
"#rating"
).
val
(
Math
.
round
(
meta
.
volumeInfo
.
averageRating
));
}
return
;
}
if
(
source
==
'douban'
)
{
meta
=
db
_r
esults
[
id
];
$
(
'#description'
).
val
(
meta
.
summary
);
$
(
'#bookAuthor'
).
val
(
meta
.
author
.
join
(
' & '
));
$
(
'#book_title'
).
val
(
meta
.
title
);
var
tags
=
''
;
if
(
source
==
=
"douban"
)
{
meta
=
db
R
esults
[
id
];
$
(
"#description"
).
val
(
meta
.
summary
);
$
(
"#bookAuthor"
).
val
(
meta
.
author
.
join
(
" & "
));
$
(
"#book_title"
).
val
(
meta
.
title
);
tags
=
""
;
for
(
var
i
=
0
;
i
<
meta
.
tags
.
length
;
i
++
)
{
tags
=
tags
+
meta
.
tags
[
i
].
title
+
','
;
tags
=
tags
+
meta
.
tags
[
i
].
title
+
","
;
}
$
(
'#tags'
).
val
(
tags
);
$
(
'#rating'
).
val
(
Math
.
round
(
meta
.
rating
.
average
/
2
));
$
(
"#tags"
).
val
(
tags
);
$
(
"#rating"
).
val
(
Math
.
round
(
meta
.
rating
.
average
/
2
));
return
;
}
}
do_search
=
function
(
keyword
)
{
show_flag
=
0
;
$
(
'#meta-info'
).
text
(
msg
.
loading
);
var
keyword
=
$
(
'#keyword'
).
val
();
if
(
keyword
)
{
db_search_book
(
keyword
);
gg_search_book
(
keyword
);
}
}
db_search_book
=
function
(
title
)
{
var
url
=
douban
+
db
_search
+
'?q='
+
title
+
'&fields=all&count=10'
;
function
dbSearchBook
(
title
)
{
var
url
=
douban
+
db
Search
+
"?q="
+
title
+
"&fields=all&count=10"
;
$
.
ajax
({
url
:
url
,
url
,
type
:
"GET"
,
dataType
:
"jsonp"
,
jsonp
:
'callback'
,
success
:
function
(
data
)
{
db
_r
esults
=
data
.
books
;
jsonp
:
"callback"
,
success
(
data
)
{
db
R
esults
=
data
.
books
;
},
error
:
function
()
{
$
(
'#meta-info'
).
html
(
'<p class="text-danger">'
+
msg
.
search_error
+
'!</p>'
);
error
()
{
$
(
"#meta-info"
).
html
(
"<p class=
\"
text-danger
\"
>"
+
msg
.
search_error
+
"!</p>"
);
},
complete
:
function
()
{
db
_d
one
=
true
;
show
_r
esult
();
complete
()
{
db
D
one
=
true
;
show
R
esult
();
}
});
}
show_result
=
function
()
{
show_flag
++
;
if
(
show_flag
==
1
)
{
$
(
'#meta-info'
).
html
(
'<ul id="book-list" class="media-list"></ul>'
);
}
if
(
gg_done
&&
db_done
)
{
if
(
!
gg_results
&&
!
db_results
)
{
$
(
'#meta-info'
).
html
(
'<p class="text-danger">'
+
msg
.
no_result
+
'</p>'
);
return
;
}
}
if
(
gg_done
&&
gg_results
.
length
>
0
)
{
for
(
var
i
=
0
;
i
<
gg_results
.
length
;
i
++
)
{
var
book
=
gg_results
[
i
];
var
book_cover
;
if
(
book
.
volumeInfo
.
imageLinks
)
{
book_cover
=
book
.
volumeInfo
.
imageLinks
.
thumbnail
;
}
else
{
book_cover
=
'/static/generic_cover.jpg'
;
}
var
book_html
=
'<li class="media">'
+
'<img class="pull-left img-responsive" data-toggle="modal" data-target="#metaModal" src="'
+
book_cover
+
'" alt="Cover" style="width:100px;height:150px" onclick=
\'
javascript:get_meta("google",'
+
i
+
')
\'
>'
+
'<div class="media-body">'
+
'<h4 class="media-heading"><a href="https://books.google.com/books?id='
+
book
.
id
+
'" target="_blank">'
+
book
.
volumeInfo
.
title
+
'</a></h4>'
+
'<p>'
+
msg
.
author
+
':'
+
book
.
volumeInfo
.
authors
+
'</p>'
+
'<p>'
+
msg
.
publisher
+
':'
+
book
.
volumeInfo
.
publisher
+
'</p>'
+
'<p>'
+
msg
.
description
+
':'
+
book
.
volumeInfo
.
description
+
'</p>'
+
'<p>'
+
msg
.
source
+
':<a href="https://books.google.com" target="_blank">Google Books</a></p>'
+
'</div>'
+
'</li>'
;
$
(
"#book-list"
).
append
(
book_html
);
}
gg_done
=
false
;
}
if
(
db_done
&&
db_results
.
length
>
0
)
{
for
(
var
i
=
0
;
i
<
db_results
.
length
;
i
++
)
{
var
book
=
db_results
[
i
];
var
book_html
=
'<li class="media">'
+
'<img class="pull-left img-responsive" data-toggle="modal" data-target="#metaModal" src="'
+
book
.
image
+
'" alt="Cover" style="width:100px;height: 150px" onclick=
\'
javascript:get_meta("douban",'
+
i
+
')
\'
>'
+
'<div class="media-body">'
+
'<h4 class="media-heading"><a href="https://book.douban.com/subject/'
+
book
.
id
+
'" target="_blank">'
+
book
.
title
+
'</a></h4>'
+
'<p>'
+
msg
.
author
+
':'
+
book
.
author
+
'</p>'
+
'<p>'
+
msg
.
publisher
+
':'
+
book
.
publisher
+
'</p>'
+
'<p>'
+
msg
.
description
+
':'
+
book
.
summary
+
'</p>'
+
'<p>'
+
msg
.
source
+
':<a href="https://book.douban.com" target="_blank">Douban Books</a></p>'
+
'</div>'
+
'</li>'
;
$
(
"#book-list"
).
append
(
book_html
);
}
db_done
=
false
;
function
doSearch
(
keyword
)
{
showFlag
=
0
;
$
(
"#meta-info"
).
text
(
msg
.
loading
);
// var keyword = $("#keyword").val();
if
(
keyword
)
{
dbSearchBook
(
keyword
);
ggSearchBook
(
keyword
);
}
}
$
(
'#do-search'
).
click
(
function
()
{
var
keyword
=
$
(
'#keyword'
).
val
();
$
(
"#do-search"
).
click
(
function
()
{
var
keyword
=
$
(
"#keyword"
).
val
();
if
(
keyword
)
{
do
_s
earch
(
keyword
);
do
S
earch
(
keyword
);
}
});
$
(
'#get_meta'
).
click
(
function
()
{
var
book
_title
=
$
(
'#book_title'
).
val
();
if
(
book
_t
itle
)
{
$
(
'#keyword'
).
val
(
book_t
itle
);
do
_search
(
book_t
itle
);
$
(
"#get_meta"
).
click
(
function
()
{
var
book
Title
=
$
(
"#book_title"
).
val
();
if
(
book
T
itle
)
{
$
(
"#keyword"
).
val
(
bookT
itle
);
do
Search
(
bookT
itle
);
}
});
...
...
cps/static/js/main.js
View file @
ccf56302
...
...
@@ -3,13 +3,47 @@ var updateTimerID;
var
updateText
;
$
(
function
()
{
$
(
'.discover .row'
).
isotope
({
function
restartTimer
()
{
$
(
"#spinner"
).
addClass
(
"hidden"
);
$
(
"#RestartDialog"
).
modal
(
"hide"
);
}
function
updateTimer
()
{
$
.
ajax
({
dataType
:
"json"
,
url
:
window
.
location
.
pathname
+
"/../../get_updater_status"
,
success
(
data
)
{
// console.log(data.status);
$
(
"#UpdateprogressDialog #Updatecontent"
).
html
(
updateText
[
data
.
status
]);
if
(
data
.
status
>
6
){
clearInterval
(
updateTimerID
);
$
(
"#spinner2"
).
hide
();
$
(
"#UpdateprogressDialog #updateFinished"
).
removeClass
(
"hidden"
);
$
(
"#check_for_update"
).
removeClass
(
"hidden"
);
$
(
"#perform_update"
).
addClass
(
"hidden"
);
}
},
error
()
{
// console.log('Done');
clearInterval
(
updateTimerID
);
$
(
"#spinner2"
).
hide
();
$
(
"#UpdateprogressDialog #Updatecontent"
).
html
(
updateText
[
7
]);
$
(
"#UpdateprogressDialog #updateFinished"
).
removeClass
(
"hidden"
);
$
(
"#check_for_update"
).
removeClass
(
"hidden"
);
$
(
"#perform_update"
).
addClass
(
"hidden"
);
},
timeout
:
2000
});
}
$
(
".discover .row"
).
isotope
({
// options
itemSelector
:
'.book'
,
layoutMode
:
'fitRows'
itemSelector
:
".book"
,
layoutMode
:
"fitRows"
});
$
(
'.load-more .row'
).
infinitescroll
({
$
(
".load-more .row"
).
infinitescroll
({
debug
:
false
,
navSelector
:
".pagination"
,
// selector for the paged navigation (it will be hidden)
...
...
@@ -20,109 +54,74 @@ $(function() {
extraScrollPx
:
300
,
// selector for all items you'll retrieve
},
function
(
data
){
$
(
'.load-more .row'
).
isotope
(
'appended'
,
$
(
data
),
null
);
$
(
".load-more .row"
).
isotope
(
"appended"
,
$
(
data
),
null
);
});
$
(
'#sendbtn'
).
click
(
function
(){
$
(
"#sendbtn"
).
click
(
function
(){
var
$this
=
$
(
this
);
$this
.
text
(
'Please wait...'
);
$this
.
addClass
(
'disabled'
);
$this
.
text
(
"Please wait..."
);
$this
.
addClass
(
"disabled"
);
});
$
(
"#restart"
).
click
(
function
()
{
$
.
ajax
({
dataType
:
'json'
,
dataType
:
"json"
,
url
:
window
.
location
.
pathname
+
"/../../shutdown"
,
data
:
{
"parameter"
:
0
},
success
:
function
(
data
)
{
$
(
'#spinner'
).
show
();
success
(
data
)
{
$
(
"#spinner"
).
show
();
displaytext
=
data
.
text
;
setTimeout
(
restartTimer
,
3000
);}
});
});
$
(
"#shutdown"
).
click
(
function
()
{
$
.
ajax
({
dataType
:
'json'
,
dataType
:
"json"
,
url
:
window
.
location
.
pathname
+
"/../../shutdown"
,
data
:
{
"parameter"
:
1
},
success
:
function
(
data
)
{
success
(
data
)
{
return
alert
(
data
.
text
);}
});
});
$
(
"#check_for_update"
).
click
(
function
()
{
var
button
_t
ext
=
$
(
"#check_for_update"
).
html
();
$
(
"#check_for_update"
).
html
(
'...'
);
var
button
T
ext
=
$
(
"#check_for_update"
).
html
();
$
(
"#check_for_update"
).
html
(
"..."
);
$
.
ajax
({
dataType
:
'json'
,
dataType
:
"json"
,
url
:
window
.
location
.
pathname
+
"/../../get_update_status"
,
success
:
function
(
data
)
{
$
(
"#check_for_update"
).
html
(
button
_t
ext
);
if
(
data
.
status
==
true
)
{
$
(
"#check_for_update"
).
addClass
(
'hidden'
);
$
(
"#perform_update"
).
removeClass
(
'hidden'
);
$
(
"#update_info"
).
removeClass
(
'hidden'
);
$
(
"#update_info"
).
find
(
'span'
).
html
(
data
.
commit
);
success
(
data
)
{
$
(
"#check_for_update"
).
html
(
button
T
ext
);
if
(
data
.
status
==
=
true
)
{
$
(
"#check_for_update"
).
addClass
(
"hidden"
);
$
(
"#perform_update"
).
removeClass
(
"hidden"
);
$
(
"#update_info"
).
removeClass
(
"hidden"
);
$
(
"#update_info"
).
find
(
"span"
).
html
(
data
.
commit
);
}
}
});
});
$
(
"#restart_database"
).
click
(
function
()
{
$
.
ajax
({
dataType
:
'json'
,
dataType
:
"json"
,
url
:
window
.
location
.
pathname
+
"/../../shutdown"
,
data
:
{
"parameter"
:
2
}
});
});
$
(
"#perform_update"
).
click
(
function
()
{
$
(
'#spinner2'
).
show
();
$
(
"#spinner2"
).
show
();
$
.
ajax
({
type
:
"POST"
,
dataType
:
'json'
,
dataType
:
"json"
,
data
:
{
start
:
"True"
},
url
:
window
.
location
.
pathname
+
"/../../get_updater_status"
,
success
:
function
(
data
)
{
updateText
=
data
.
text
success
(
data
)
{
updateText
=
data
.
text
;
$
(
"#UpdateprogressDialog #Updatecontent"
).
html
(
updateText
[
data
.
status
]);
console
.
log
(
data
.
status
);
//
console.log(data.status);
updateTimerID
=
setInterval
(
updateTimer
,
2000
);}
});
});
});
function
restartTimer
()
{
$
(
'#spinner'
).
hide
();
$
(
'#RestartDialog'
).
modal
(
'hide'
);
}
function
updateTimer
()
{
$
.
ajax
({
dataType
:
'json'
,
url
:
window
.
location
.
pathname
+
"/../../get_updater_status"
,
success
:
function
(
data
)
{
console
.
log
(
data
.
status
);
$
(
"#UpdateprogressDialog #Updatecontent"
).
html
(
updateText
[
data
.
status
]);
if
(
data
.
status
>
6
){
clearInterval
(
updateTimerID
);
$
(
'#spinner2'
).
hide
();
$
(
'#UpdateprogressDialog #updateFinished'
).
removeClass
(
'hidden'
);
$
(
"#check_for_update"
).
removeClass
(
'hidden'
);
$
(
"#perform_update"
).
addClass
(
'hidden'
);
}
},
error
:
function
()
{
console
.
log
(
'Done'
);
clearInterval
(
updateTimerID
);
$
(
'#spinner2'
).
hide
();
$
(
"#UpdateprogressDialog #Updatecontent"
).
html
(
updateText
[
7
]);
$
(
'#UpdateprogressDialog #updateFinished'
).
removeClass
(
'hidden'
);
$
(
"#check_for_update"
).
removeClass
(
'hidden'
);
$
(
"#perform_update"
).
addClass
(
'hidden'
);
},
timeout
:
2000
$
(
window
).
resize
(
function
(
event
)
{
$
(
".discover .row"
).
isotope
(
"reLayout"
);
});
}
$
(
window
).
resize
(
function
(
event
)
{
$
(
'.discover .row'
).
isotope
(
'reLayout'
);
});
\ No newline at end of file
cps/static/js/shelforder.js
View file @
ccf56302
Sortable
.
create
(
sortTrue
,
{
/* global Sortable,sortTrue */
var
sortable
=
Sortable
.
create
(
sortTrue
,
{
group
:
"sorting"
,
sort
:
true
});
...
...
@@ -9,7 +11,7 @@ function sendData(path){
var
maxElements
;
var
tmp
=
[];
elements
=
S
ortable
.
utils
.
find
(
sortTrue
,
"div"
);
elements
=
s
ortable
.
utils
.
find
(
sortTrue
,
"div"
);
maxElements
=
elements
.
length
;
var
form
=
document
.
createElement
(
"form"
);
...
...
cps/templates/book_edit.html
View file @
ccf56302
...
...
@@ -106,7 +106,7 @@
</div>
<a
href=
"#"
id=
"get_meta"
class=
"btn btn-default"
data-toggle=
"modal"
data-target=
"#metaModal"
>
{{_('Get metadata')}}
</a>
<button
type=
"submit"
class=
"btn btn-default"
>
{{_('Submit')}}
</button>
<a
href=
"{{ url_for('show_book',id=book.id) }}"
class=
"btn btn-default"
>
{{_('Back')}}
</a>
<a
href=
"{{ url_for('show_book',
book_
id=book.id) }}"
class=
"btn btn-default"
>
{{_('Back')}}
</a>
</form>
</div>
{% endif %}
...
...
@@ -138,7 +138,7 @@
{% block js %}
<script>
var
i18n
_m
sg
=
{
var
i18n
M
sg
=
{
'loading'
:
{{
_
(
'Loading...'
)
|
safe
|
tojson
}},
'search_error'
:
{{
_
(
'Search error!'
)
|
safe
|
tojson
}},
'no_result'
:
{{
_
(
'No Result! Please try anonther keyword.'
)
|
safe
|
tojson
}},
...
...
cps/templates/detail.html
View file @
ccf56302
...
...
@@ -15,7 +15,7 @@
<h2>
{{entry.title}}
</h2>
<p
class=
"author"
>
{% for author in entry.authors %}
<a
href=
"{{url_for('author', id=author.id ) }}"
>
{{author.name}}
</a>
<a
href=
"{{url_for('author',
book_
id=author.id ) }}"
>
{{author.name}}
</a>
{% if not loop.last %}
&
{% endif %}
...
...
@@ -37,7 +37,7 @@
{% endif %}
{% if entry.series|length > 0 %}
<p>
{{_('Book')}} {{entry.series_index}} {{_('of')}}
<a
href=
"{{url_for('series', id=entry.series[0].id)}}"
>
{{entry.series[0].name}}
</a></p>
<p>
{{_('Book')}} {{entry.series_index}} {{_('of')}}
<a
href=
"{{url_for('series',
book_
id=entry.series[0].id)}}"
>
{{entry.series[0].name}}
</a></p>
{% endif %}
{% if entry.languages.__len__() > 0 %}
...
...
@@ -65,7 +65,7 @@
<span
class=
"glyphicon glyphicon-tags"
></span>
{% for tag in entry.tags %}
<a
href=
"{{ url_for('category', id=tag.id) }}"
class=
"btn btn-xs btn-info"
role=
"button"
>
{{tag.name}}
</a>
<a
href=
"{{ url_for('category',
book_
id=tag.id) }}"
class=
"btn btn-xs btn-info"
role=
"button"
>
{{tag.name}}
</a>
{%endfor%}
</div>
</p>
...
...
@@ -110,7 +110,7 @@
{% if not g.user.is_anonymous() %}
<p>
<div
class=
"custom_columns"
id=
"have_read_container"
>
<form
id=
"have_read_form"
action=
"{{ url_for('toggle_read', id=entry.id)}}"
method=
"POST"
)
>
<form
id=
"have_read_form"
action=
"{{ url_for('toggle_read',
book_
id=entry.id)}}"
method=
"POST"
)
>
<input
id=
"have_read_cb"
type=
"checkbox"
{%
if
have_read
%}
checked
{%
endif
%}
>
<label
for=
"have_read_cb"
>
{{_('Read')}}
</label>
</form>
...
...
@@ -136,7 +136,7 @@
</button>
<ul
class=
"dropdown-menu"
aria-labelledby=
"btnGroupDrop1"
>
{% for format in entry.data %}
<li><a
href=
"{{ url_for('get_download_link_ext', book_id=entry.id, format=format.format|lower, anyname=entry.id|string+'.'+format.format) }}"
>
{{format.format}}
</a></li>
<li><a
href=
"{{ url_for('get_download_link_ext', book_id=entry.id,
book_
format=format.format|lower, anyname=entry.id|string+'.'+format.format) }}"
>
{{format.format}}
</a></li>
{%endfor%}
</ul>
</div>
...
...
@@ -154,7 +154,7 @@
<ul
class=
"dropdown-menu"
aria-labelledby=
"btnGroupDrop2"
>
{% for format in entry.data %}
{%if format.format|lower == 'epub' or format.format|lower == 'txt' or format.format|lower == 'pdf' or format.format|lower == 'cbr' or format.format|lower == 'cbt' or format.format|lower == 'cbz' %}
<li><a
target=
"_blank"
href=
"{{ url_for('read_book', book_id=entry.id, format=format.format|lower) }}"
>
{{format.format}}
</a></li>
<li><a
target=
"_blank"
href=
"{{ url_for('read_book', book_id=entry.id,
book_
format=format.format|lower) }}"
>
{{format.format}}
</a></li>
{% endif %}
{%endfor%}
</ul>
...
...
cps/templates/discover.html
View file @
ccf56302
...
...
@@ -8,14 +8,14 @@
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
{% if entry.has_cover is defined %}
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
<img
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
/>
</a>
{% endif %}
</div>
<div
class=
"meta"
>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"author"
><a
href=
"{{url_for('author', id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
<p
class=
"author"
><a
href=
"{{url_for('author',
book_
id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
{% if entry.ratings.__len__() > 0 %}
<div
class=
"rating"
>
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
...
...
cps/templates/feed.xml
View file @
ccf56302
...
...
@@ -56,7 +56,7 @@
<link
type=
"image/jpeg"
href=
"{{url_for('feed_get_cover', book_id=entry.id)}}"
rel=
"http://opds-spec.org/image/thumbnail"
/>
{% endif %}
{% for format in entry.data %}
<link
rel=
"http://opds-spec.org/acquisition"
href=
"{{ url_for('get_opds_download_link', book_id=entry.id, format=format.format|lower)}}"
<link
rel=
"http://opds-spec.org/acquisition"
href=
"{{ url_for('get_opds_download_link', book_id=entry.id,
book_
format=format.format|lower)}}"
length=
"{{format.uncompressed_size}}"
mtime=
"{{entry.timestamp}}"
type=
"{{format.format|lower|mimetype}}"
/>
{% endfor %}
</entry>
...
...
@@ -65,9 +65,9 @@
{% for entry in listelements %}
<entry>
<title>
{{entry.name}}
</title>
<id>
{{ url_for(folder, id=entry.id) }}
</id>
<link
type=
"application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"
href=
"{{url_for(folder, id=entry.id)}}"
/>
<link
type=
"application/atom+xml"
href=
"{{url_for(folder, id=entry.id)}}"
rel=
"subsection"
/>
<id>
{{ url_for(folder,
book_
id=entry.id) }}
</id>
<link
type=
"application/atom+xml;profile=opds-catalog;type=feed;kind=acquisition"
href=
"{{url_for(folder,
book_
id=entry.id)}}"
/>
<link
type=
"application/atom+xml"
href=
"{{url_for(folder,
book_
id=entry.id)}}"
rel=
"subsection"
/>
</entry>
{% endfor %}
</feed>
cps/templates/index.html
View file @
ccf56302
...
...
@@ -8,7 +8,7 @@
{% for entry in random %}
<div
id=
"books_rand"
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
{% if entry.has_cover %}
<img
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
/>
{% else %}
...
...
@@ -18,7 +18,7 @@
</div>
<div
class=
"meta"
>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"author"
><a
href=
"{{url_for('author', id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
<p
class=
"author"
><a
href=
"{{url_for('author',
book_
id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
{% if entry.ratings.__len__() > 0 %}
<div
class=
"rating"
>
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
...
...
@@ -44,7 +44,7 @@
{% for entry in entries %}
<div
id=
"books"
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
{% if entry.has_cover %}
<img
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
/>
{% else %}
...
...
@@ -56,7 +56,7 @@
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"author"
>
{% for author in entry.authors %}
<a
href=
"{{url_for('author', id=author.id) }}"
>
{{author.name}}
</a>
<a
href=
"{{url_for('author',
book_
id=author.id) }}"
>
{{author.name}}
</a>
{% if not loop.last %}
&
{% endif %}
...
...
cps/templates/json.txt
View file @
ccf56302
...
...
@@ -36,7 +36,7 @@
"timestamp": "{{entry.timestamp}}",
"thumbnail": "{{url_for('feed_get_cover', book_id=entry.id)}}",
"main_format": {
"{{entry.data[0].format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id, format=entry.data[0].format|lower)}}"
"{{entry.data[0].format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id,
book_
format=entry.data[0].format|lower)}}"
},
"rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
"authors": [
...
...
@@ -47,7 +47,7 @@
"other_formats": {
{% if entry.data.__len__() > 1 %}
{% for format in entry.data[1:] %}
"{{format.format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id, format=format.format|lower)}}"{% if not loop.last %},{% endif %}
"{{format.format|lower}}": "{{ url_for('get_opds_download_link', book_id=entry.id,
book_
format=format.format|lower)}}"{% if not loop.last %},{% endif %}
{% endfor %}
{% endif %} },
"title_sort": "{{entry.sort}}"
...
...
cps/templates/list.html
View file @
ccf56302
...
...
@@ -10,7 +10,7 @@
{% endif %}
<div
class=
"row"
>
<div
class=
"col-xs-1"
align=
"left"
><span
class=
"badge"
>
{{entry.count}}
</span></div>
<div
class=
"col-xs-6"
><a
id=
"list_{{loop.index0}}"
href=
"{{url_for(folder, id=entry[0].id )}}"
>
{{entry[0].name}}
</a></div>
<div
class=
"col-xs-6"
><a
id=
"list_{{loop.index0}}"
href=
"{{url_for(folder,
book_
id=entry[0].id )}}"
>
{{entry[0].name}}
</a></div>
</div>
{% endfor %}
</div>
...
...
cps/templates/search.html
View file @
ccf56302
...
...
@@ -15,7 +15,7 @@
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
{% if entry.has_cover is defined %}
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
<img
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
/>
</a>
{% endif %}
...
...
@@ -24,7 +24,7 @@
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"author"
>
{% for author in entry.authors %}
<a
href=
"{{url_for('author', id=author.id ) }}"
>
{{author.name}}
</a>
<a
href=
"{{url_for('author',
book_
id=author.id ) }}"
>
{{author.name}}
</a>
{% if not loop.last %}
&
{% endif %}
...
...
cps/templates/shelf.html
View file @
ccf56302
...
...
@@ -15,14 +15,14 @@
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
{% if entry.has_cover is defined %}
<a
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
<img
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
/>
</a>
{% endif %}
</div>
<div
class=
"meta"
>
<p
class=
"title"
>
{{entry.title|shortentitle}}
</p>
<p
class=
"author"
><a
href=
"{{url_for('author', id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
<p
class=
"author"
><a
href=
"{{url_for('author',
book_
id=entry.authors[0].id) }}"
>
{{entry.authors[0].name}}
</a></p>
{% if entry.ratings.__len__() > 0 %}
<div
class=
"rating"
>
{% for number in range((entry.ratings[0].rating/2)|int(2)) %}
...
...
cps/templates/user_edit.html
View file @
ccf56302
...
...
@@ -131,7 +131,7 @@
<h2>
{{_('Recent Downloads')}}
</h2>
{% for entry in downloads %}
<div
class=
"col-sm-2"
>
<a
class=
"pull-left"
href=
"{{ url_for('show_book', id=entry.id) }}"
>
<a
class=
"pull-left"
href=
"{{ url_for('show_book',
book_
id=entry.id) }}"
>
<img
class=
"media-object"
width=
"100"
src=
"{{ url_for('get_cover', cover_path=entry.path.replace('\\','/')) }}"
alt=
"..."
>
</a>
</div>
...
...
cps/translations/es/LC_MESSAGES/messages.mo
View file @
ccf56302
No preview for this file type
cps/translations/es/LC_MESSAGES/messages.po
View file @
ccf56302
...
...
@@ -15,15 +15,16 @@ msgstr ""
"Project-Id-Version: Calibre-web\n"
"Report-Msgid-Bugs-To: https://github.com/janeczku/calibre-web\n"
"POT-Creation-Date: 2017-03-19 19:20+0100\n"
"PO-Revision-Date: 201
6-11-13 18:35+01
00\n"
"PO-Revision-Date: 201
7-04-04 15:09+02
00\n"
"Last-Translator: Juan F. Villa <juan.villa@paisdelconocimiento.org>\n"
"Language: es\n"
"Language-Team: \n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"Plural-Forms: nplurals=2; plural=(n != 1)
;
\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 1.8.7.1\n"
#: cps/book_formats.py:113 cps/book_formats.py:117 cps/web.py:1244
msgid "not installed"
...
...
@@ -56,35 +57,35 @@ msgstr "No fue posible convertir de epub a mobi"
#: cps/ub.py:488
msgid "Guest"
msgstr ""
msgstr "
Invitado
"
#: cps/web.py:904
msgid "Requesting update package"
msgstr ""
msgstr "
Solicitando paquete de actualización
"
#: cps/web.py:905
msgid "Downloading update package"
msgstr ""
msgstr "
Descargando paquete de actualización
"
#: cps/web.py:906
msgid "Unzipping update package"
msgstr ""
msgstr "
Descomprimiendo paquete de actualización
"
#: cps/web.py:907
msgid "Files are replaced"
msgstr ""
msgstr "
Ficheros sustituidos
"
#: cps/web.py:908
msgid "Database connections are closed"
msgstr ""
msgstr "
Los conexiones de base datos están cerradas
"
#: cps/web.py:909
msgid "Server is stopped"
msgstr ""
msgstr "
El servidor está detenido
"
#: cps/web.py:910
msgid "Update finished, please press okay and reload page"
msgstr ""
msgstr "
Actualización finalizada. Por favor, pulse OK y recargue la página
"
#: cps/web.py:983
msgid "Latest Books"
...
...
@@ -92,33 +93,33 @@ msgstr "Libros recientes"
#: cps/web.py:1014
msgid "Hot Books (most downloaded)"
msgstr "Libros
P
opulares (los mas descargados)"
msgstr "Libros
p
opulares (los mas descargados)"
#: cps/web.py:1024
msgid "Best rated books"
msgstr ""
msgstr "
Libros mejor valorados
"
#: cps/templates/index.xml:36 cps/web.py:1033
msgid "Random Books"
msgstr "Libros al
A
zar"
msgstr "Libros al
a
zar"
#: cps/web.py:1046
msgid "Author list"
msgstr "Lista de
A
utores"
msgstr "Lista de
a
utores"
#: cps/web.py:1057
#, python-format
msgid "Author: %(name)s"
msgstr ""
msgstr "
Autor:%(name)s
"
#: cps/web.py:1059 cps/web.py:1087 cps/web.py:1221 cps/web.py:1626
#: cps/web.py:2579
msgid "Error opening eBook. File does not exist or file is not accessible:"
msgstr "Error en
apertura del Objeto. El archivo no existe o no es accesible
"
msgstr "Error en
la apertura del eBook. El archivo no existe o no es accesible:
"
#: cps/templates/index.xml:71 cps/web.py:1073
msgid "Series list"
msgstr "
lista de S
eries"
msgstr "
Lista de s
eries"
#: cps/web.py:1085
#, python-format
...
...
@@ -136,12 +137,12 @@ msgstr "Lenguaje: %(name)s"
#: cps/templates/index.xml:64 cps/web.py:1146
msgid "Category list"
msgstr "Lista de
C
ategorias"
msgstr "Lista de
c
ategorias"
#: cps/web.py:1158
#, python-format
msgid "Category: %(name)s"
msgstr "Categor
i
a : %(name)s"
msgstr "Categor
í
a : %(name)s"
#: cps/web.py:1267
msgid "Statistics"
...
...
@@ -149,39 +150,39 @@ msgstr "Estadisticas"
#: cps/web.py:1375
msgid "Server restarted, please reload page"
msgstr ""
msgstr "
Servidor reiniciado. Por favor, recargue la página
"
#: cps/web.py:1377
msgid "Performing shutdown of server, please close window"
msgstr ""
msgstr "
Servidor en proceso de apagado. Por favor, cierre la ventana.
"
#: cps/web.py:1392
msgid "Update done"
msgstr ""
msgstr "
Actualización realizada
"
#: cps/web.py:1470 cps/web.py:1483
msgid "search"
msgstr ""
msgstr "
búsqueda
"
#: cps/web.py:1602 cps/web.py:1609 cps/web.py:1616 cps/web.py:1623
msgid "Read a Book"
msgstr "Leer un
L
ibro"
msgstr "Leer un
l
ibro"
#: cps/web.py:1676 cps/web.py:2152
msgid "Please fill out all fields!"
msgstr "
Por favor llen
ar todos los campos!"
msgstr "
¡Por favor complet
ar todos los campos!"
#: cps/web.py:1677 cps/web.py:1693 cps/web.py:1698 cps/web.py:1700
msgid "register"
msgstr "
R
egistrarse"
msgstr "
r
egistrarse"
#: cps/web.py:1692
msgid "An unknown error occured. Please try again later."
msgstr "
Ocurrio un error. Intentar
de nuevo mas tarde."
msgstr "
Error desconocido. Por favor, inténtelo
de nuevo mas tarde."
#: cps/web.py:1697
msgid "This username or email address is already in use."
msgstr "Usuario o direcci
o
n de correo en uso."
msgstr "Usuario o direcci
ó
n de correo en uso."
#: cps/web.py:1715
#, python-format
...
...
@@ -194,7 +195,7 @@ msgstr "Usuario o contraseña invalido"
#: cps/web.py:1722
msgid "login"
msgstr "Iniciar
Sesio
n"
msgstr "Iniciar
sesió
n"
#: cps/web.py:1739
msgid "Please configure the SMTP mail settings first..."
...
...
@@ -236,20 +237,20 @@ msgstr "Estante %(title)s creado"
#: cps/web.py:1819 cps/web.py:1847
msgid "There was an error"
msgstr "H
emos ten
ido un error"
msgstr "H
a suced
ido un error"
#: cps/web.py:1820 cps/web.py:1822
msgid "create a shelf"
msgstr "
Crear un E
stante"
msgstr "
crear un e
stante"
#: cps/web.py:1845
#, python-format
msgid "Shelf %(title)s changed"
msgstr ""
msgstr "
Estante %(title)s cambiado
"
#: cps/web.py:1848 cps/web.py:1850
msgid "Edit a shelf"
msgstr ""
msgstr "
Editar un estante
"
#: cps/web.py:1868
#, python-format
...
...
@@ -264,11 +265,11 @@ msgstr "Estante: '%(name)s'"
#: cps/web.py:1921
#, python-format
msgid "Change order of Shelf: '%(name)s'"
msgstr ""
msgstr "
Cambiar orden del estante: '%(name)s'
"
#: cps/web.py:1985
msgid "Found an existing account for this email address."
msgstr "Existe una cuenta vinculada a esta
cuenta
de correo."
msgstr "Existe una cuenta vinculada a esta
dirección
de correo."
#: cps/web.py:1987 cps/web.py:1991
#, python-format
...
...
@@ -281,19 +282,19 @@ msgstr "Perfil actualizado"
#: cps/web.py:2002
msgid "Admin page"
msgstr ""
msgstr "
Página de administración
"
#: cps/web.py:2106
msgid "Calibre-web configuration updated"
msgstr ""
msgstr "
Configuración de Calibre-web actualizada
"
#: cps/web.py:2113 cps/web.py:2119 cps/web.py:2133
msgid "Basic Configuration"
msgstr ""
msgstr "
Configuración básica
"
#: cps/web.py:2117
msgid "DB location is not valid, please enter correct path"
msgstr ""
msgstr "
Localicación de la BD inválida. Por favor, introduzca la ruta correcta.
"
#: cps/templates/admin.html:34 cps/web.py:2154 cps/web.py:2202
msgid "Add new user"
...
...
@@ -306,11 +307,11 @@ msgstr "Usuario '%(user)s' creado"
#: cps/web.py:2198
msgid "Found an existing account for this email address or nickname."
msgstr "Se ha encontrado una cuenta vinculada a esta
cuenta de correo o
usuario."
msgstr "Se ha encontrado una cuenta vinculada a esta
dirección de correo o nombre de
usuario."
#: cps/web.py:2220
msgid "Mail settings updated"
msgstr "Par
a
metros de correo actualizados"
msgstr "Par
á
metros de correo actualizados"
#: cps/web.py:2227
#, python-format
...
...
@@ -324,7 +325,7 @@ msgstr "Error al realizar envio de prueba a E-Mail: %(res)s"
#: cps/web.py:2234
msgid "E-Mail settings updated"
msgstr ""
msgstr "
Ajustes de correo electrónico actualizados
"
#: cps/web.py:2235
msgid "Edit mail settings"
...
...
@@ -338,11 +339,11 @@ msgstr "Usuario '%(nick)s' borrado"
#: cps/web.py:2349
#, python-format
msgid "User '%(nick)s' updated"
msgstr "Usuario '%(nick)s'
A
ctualizado"
msgstr "Usuario '%(nick)s'
a
ctualizado"
#: cps/web.py:2352
msgid "An unknown error occured."
msgstr "
Oups !
Error inesperado."
msgstr "Error inesperado."
#: cps/web.py:2355
#, python-format
...
...
@@ -351,16 +352,16 @@ msgstr "Editar Usuario %(nick)s"
#: cps/web.py:2574 cps/web.py:2577 cps/web.py:2689
msgid "edit metadata"
msgstr ""
msgstr "
editar metainformación
"
#: cps/web.py:2598
#, python-format
msgid "File extension \"%s\" is not allowed to be uploaded to this server"
msgstr ""
msgstr "
No se permite subir archivos con la extensión \"%s\" a este servidor
"
#: cps/web.py:2604
msgid "File to be uploaded must have an extension"
msgstr ""
msgstr "
El archivo a subir debe tener una extensión
"
#: cps/web.py:2621
#, python-format
...
...
@@ -399,7 +400,7 @@ msgstr "DLS"
#: cps/templates/admin.html:12 cps/templates/layout.html:85
msgid "Admin"
msgstr "Administraci
o
n"
msgstr "Administraci
ó
n"
#: cps/templates/admin.html:13 cps/templates/detail.html:134
msgid "Download"
...
...
@@ -419,7 +420,7 @@ msgstr "Clave"
#: cps/templates/admin.html:35
msgid "SMTP mail settings"
msgstr "Par
a
metros smtp del correo"
msgstr "Par
á
metros smtp del correo"
#: cps/templates/admin.html:38 cps/templates/email_edit.html:7
msgid "SMTP hostname"
...
...
@@ -451,76 +452,76 @@ msgstr "Cambiar parametros smtp"
#: cps/templates/admin.html:57 cps/templates/admin.html:77
msgid "Configuration"
msgstr ""
msgstr "
Configuración
"
#: cps/templates/admin.html:60
msgid "Calibre DB dir"
msgstr ""
msgstr "
Dir DB Calibre
"
#: cps/templates/admin.html:61 cps/templates/config_edit.html:76
msgid "Log Level"
msgstr ""
msgstr "
Nivel de registro
"
#: cps/templates/admin.html:62
msgid "Port"
msgstr ""
msgstr "
Puerto
"
#: cps/templates/admin.html:63 cps/templates/config_edit.html:60
msgid "Books per page"
msgstr ""
msgstr "
Libros por página
"
#: cps/templates/admin.html:64
msgid "Uploading"
msgstr ""
msgstr "
Subiendo
"
#: cps/templates/admin.html:65
msgid "Public registration"
msgstr ""
msgstr "
Registro público
"
#: cps/templates/admin.html:66
msgid "Anonymous browsing"
msgstr ""
msgstr "
Navegación anónima
"
#: cps/templates/admin.html:78
msgid "Administration"
msgstr ""
msgstr "
Administración
"
#: cps/templates/admin.html:80
msgid "Current commit timestamp"
msgstr ""
msgstr "
Marca temporal del commit actual
"
#: cps/templates/admin.html:81
msgid "Newest commit timestamp"
msgstr ""
msgstr "
Marca temporal del commit más reciente
"
#: cps/templates/admin.html:83
msgid "Reconnect to Calibre DB"
msgstr ""
msgstr "
Reconectar la BD Calibre
"
#: cps/templates/admin.html:84
msgid "Restart Calibre-web"
msgstr ""
msgstr "
Reinicial Calibre-web
"
#: cps/templates/admin.html:85
msgid "Stop Calibre-web"
msgstr ""
msgstr "
Detener Calibre-web
"
#: cps/templates/admin.html:86
msgid "Check for update"
msgstr ""
msgstr "
Buscar actualizaciones
"
#: cps/templates/admin.html:87
msgid "Perform Update"
msgstr ""
msgstr "
Actualizar
"
#: cps/templates/admin.html:97
msgid "Do you really want to restart Calibre-web?"
msgstr ""
msgstr "
¿Seguro que quiere reiniciar Calibre-web?
"
#: cps/templates/admin.html:102 cps/templates/admin.html:116
#: cps/templates/admin.html:137
msgid "Ok"
msgstr ""
msgstr "
Ok
"
#: cps/templates/admin.html:103 cps/templates/admin.html:117
#: cps/templates/book_edit.html:109 cps/templates/config_edit.html:119
...
...
@@ -531,11 +532,11 @@ msgstr "Regresar"
#: cps/templates/admin.html:115
msgid "Do you really want to stop Calibre-web?"
msgstr ""
msgstr "
¿Seguro que quiere detener Calibre-web?
"
#: cps/templates/admin.html:128
msgid "Updating, please do not reload page"
msgstr ""
msgstr "
Actualizando. Por favor, no recargue la página.
"
#: cps/templates/book_edit.html:16 cps/templates/search_form.html:6
msgid "Book Title"
...
...
@@ -589,7 +590,7 @@ msgstr "Ver libro tras la edicion"
#: cps/templates/book_edit.html:107 cps/templates/book_edit.html:118
msgid "Get metadata"
msgstr ""
msgstr "
Obtener metainformación
"
#: cps/templates/book_edit.html:108 cps/templates/config_edit.html:117
#: cps/templates/login.html:19 cps/templates/search_form.html:79
...
...
@@ -599,76 +600,76 @@ msgstr "Enviar"
#: cps/templates/book_edit.html:121
msgid "Keyword"
msgstr ""
msgstr "
Palabra clave
"
#: cps/templates/book_edit.html:122
msgid " Search keyword "
msgstr ""
msgstr "
Buscar palabras clave
"
#: cps/templates/book_edit.html:124 cps/templates/layout.html:60
msgid "Go!"
msgstr "Vamos!"
msgstr "
¡
Vamos!"
#: cps/templates/book_edit.html:125
msgid "Click the cover to load metadata to the form"
msgstr ""
msgstr "
Haga clic en la portada para cargar la metainformación en el formulario
"
#: cps/templates/book_edit.html:129 cps/templates/book_edit.html:142
msgid "Loading..."
msgstr ""
msgstr "
Cargando...
"
#: cps/templates/book_edit.html:132
msgid "Close"
msgstr ""
msgstr "
Cerrar
"
#: cps/templates/book_edit.html:143
msgid "Search error!"
msgstr ""
msgstr "
¡Error en la búsqueda!
"
#: cps/templates/book_edit.html:144
msgid "No Result! Please try anonther keyword."
msgstr ""
msgstr "
¡Sin resultados! Por favor, pruebe otra palabra clave.
"
#: cps/templates/book_edit.html:146 cps/templates/detail.html:76
#: cps/templates/search_form.html:14
msgid "Publisher"
msgstr ""
msgstr "
Editor
"
#: cps/templates/book_edit.html:148
msgid "Source"
msgstr ""
msgstr "
Origen
"
#: cps/templates/config_edit.html:7
msgid "Location of Calibre database"
msgstr ""
msgstr "
Ubicación de la base de datos Calibre
"
#: cps/templates/config_edit.html:13
msgid "Use google drive?"
msgstr ""
msgstr "
¿Utiliza google drive?
"
#: cps/templates/config_edit.html:17
msgid "Client id"
msgstr ""
msgstr "
Id cliente
"
#: cps/templates/config_edit.html:21
msgid "Client secret"
msgstr ""
msgstr "
Contraseña cliente
"
#: cps/templates/config_edit.html:25
msgid "Calibre Base URL"
msgstr ""
msgstr "
URL Base de Calibre
"
#: cps/templates/config_edit.html:29
msgid "Google drive Calibre folder"
msgstr ""
msgstr "
Carpeta Calibre de Google drive
"
#: cps/templates/config_edit.html:38
msgid "Metadata Watch Channel ID"
msgstr ""
msgstr "
Metadata Watch Channel ID
"
#: cps/templates/config_edit.html:52
msgid "Server Port"
msgstr ""
msgstr "
Puerto del servidor
"
#: cps/templates/config_edit.html:56 cps/templates/shelf_edit.html:7
msgid "Title"
...
...
@@ -676,31 +677,31 @@ msgstr "Titulo"
#: cps/templates/config_edit.html:64
msgid "No. of random books to show"
msgstr ""
msgstr "
Número de libros aletorios a mostrar
"
#: cps/templates/config_edit.html:68
msgid "Regular expression for ignoring columns"
msgstr ""
msgstr "
Expresión regular para ignorar columnas
"
#: cps/templates/config_edit.html:72
msgid "Regular expression for title sorting"
msgstr ""
msgstr "
Expresión regular para ordenar títulos
"
#: cps/templates/config_edit.html:86
msgid "Enable uploading"
msgstr ""
msgstr "
Permitir subida
"
#: cps/templates/config_edit.html:90
msgid "Enable anonymous browsing"
msgstr ""
msgstr "
Permitir navegación anónima
"
#: cps/templates/config_edit.html:94
msgid "Enable public registration"
msgstr ""
msgstr "
Permitir registro público
"
#: cps/templates/config_edit.html:96
msgid "Default Settings for new users"
msgstr ""
msgstr "
Ajustes por defecto para nuevos usuarios
"
#: cps/templates/config_edit.html:99 cps/templates/user_edit.html:87
msgid "Admin user"
...
...
@@ -741,11 +742,11 @@ msgstr "Lenguaje"
#: cps/templates/detail.html:81
msgid "Publishing date"
msgstr ""
msgstr "
Fecha de publicación
"
#: cps/templates/detail.html:115
msgid "Read"
msgstr ""
msgstr "
Leer
"
#: cps/templates/detail.html:123
msgid "Description:"
...
...
@@ -765,23 +766,23 @@ msgstr "Editar la metadata"
#: cps/templates/email_edit.html:11
msgid "SMTP port (usually 25 for plain SMTP and 465 for SSL and 587 for STARTTLS)"
msgstr ""
msgstr "
Puerto SMTP (por lo general 25 para SMTP plano, 465 para SSL y 587 para STARTTLS)
"
#: cps/templates/email_edit.html:15
msgid "Encryption"
msgstr ""
msgstr "
Cifrado
"
#: cps/templates/email_edit.html:17
msgid "None"
msgstr ""
msgstr "
Ninguno
"
#: cps/templates/email_edit.html:18
msgid "STARTTLS"
msgstr ""
msgstr "
STATRTTLS
"
#: cps/templates/email_edit.html:19
msgid "SSL/TLS"
msgstr ""
msgstr "
SSL/TLS
"
#: cps/templates/email_edit.html:31
msgid "From e-mail"
...
...
@@ -817,11 +818,11 @@ msgstr "Libros Populares"
#: cps/templates/index.xml:19
msgid "Popular publications from this catalog based on Downloads."
msgstr ""
msgstr "
Publicaciones mas populares para este catálogo basadas en las descargas.
"
#: cps/templates/index.xml:22 cps/templates/layout.html:129
msgid "Best rated Books"
msgstr ""
msgstr "
Libros mejor valorados
"
#: cps/templates/index.xml:26
msgid "Popular publications from this catalog based on Rating."
...
...
@@ -829,11 +830,11 @@ msgstr "Publicaciones populares del catalogo basados en el puntaje."
#: cps/templates/index.xml:29 cps/templates/layout.html:124
msgid "New Books"
msgstr "Nuevos
L
ibros"
msgstr "Nuevos
l
ibros"
#: cps/templates/index.xml:33
msgid "The latest Books"
msgstr "Libros
R
ecientes"
msgstr "Libros
r
ecientes"
#: cps/templates/index.xml:40
msgid "Show Random Books"
...
...
@@ -842,12 +843,12 @@ msgstr "Mostrar libros al azar"
#: cps/templates/index.xml:43 cps/templates/index.xml:47
#: cps/templates/layout.html:132
msgid "Read Books"
msgstr ""
msgstr "
Libros leídos
"
#: cps/templates/index.xml:50 cps/templates/index.xml:54
#: cps/templates/layout.html:133
msgid "Unread Books"
msgstr ""
msgstr "
Libros no leídos
"
#: cps/templates/index.xml:57 cps/templates/layout.html:144
msgid "Authors"
...
...
@@ -867,7 +868,7 @@ msgstr "Libros ordenados por Series"
#: cps/templates/layout.html:48
msgid "Toggle navigation"
msgstr ""
msgstr "
Alternar navegación
"
#: cps/templates/layout.html:68
msgid "Advanced Search"
...
...
@@ -875,7 +876,7 @@ msgstr "Busqueda avanzada"
#: cps/templates/layout.html:89
msgid "Logout"
msgstr "Cerrar
Sesio
n"
msgstr "Cerrar
sesió
n"
#: cps/templates/layout.html:94 cps/templates/register.html:18
msgid "Register"
...
...
@@ -899,11 +900,11 @@ msgstr "Lenguaje"
#: cps/templates/layout.html:149
msgid "Public Shelves"
msgstr "Estantes
Pu
blicos"
msgstr "Estantes
pú
blicos"
#: cps/templates/layout.html:153
msgid "Your Shelves"
msgstr "Sus
E
stantes"
msgstr "Sus
e
stantes"
#: cps/templates/layout.html:158
msgid "Create a Shelf"
...
...
@@ -929,7 +930,7 @@ msgstr "Recordarme"
#: cps/templates/osd.xml:5
msgid "Calibre Web ebook catalog"
msgstr ""
msgstr "
Catálogo de libros electrónicos Calibre Web
"
#: cps/templates/read.html:136
msgid "Reflow text when sidebars are open."
...
...
@@ -941,7 +942,7 @@ msgstr "Visor PDF.js"
#: cps/templates/readtxt.html:6
msgid "Basic txt Reader"
msgstr ""
msgstr "
Lector básico de txt
"
#: cps/templates/register.html:4
msgid "Register a new account"
...
...
@@ -981,11 +982,11 @@ msgstr "Excluir etiquetas"
#: cps/templates/search_form.html:47
msgid "Exclude Series"
msgstr ""
msgstr "
Excluir series
"
#: cps/templates/search_form.html:68
msgid "Exclude Languages"
msgstr ""
msgstr "
Excluir idiomas
"
#: cps/templates/shelf.html:6
msgid "Delete this Shelf"
...
...
@@ -997,15 +998,15 @@ msgstr "Editar nombre del estante"
#: cps/templates/shelf.html:8 cps/templates/shelf_order.html:11
msgid "Change order"
msgstr ""
msgstr "
Cambiar orden
"
#: cps/templates/shelf_edit.html:12
msgid "should the shelf be public?"
msgstr "
Debe ser el estante publico
?"
msgstr "
¿Hacer público el estante
?"
#: cps/templates/shelf_order.html:5
msgid "Drag 'n drop to rearrange order"
msgstr ""
msgstr "
Pinchar y arrastrar para reordenar
"
#: cps/templates/stats.html:3
msgid "Calibre library statistics"
...
...
@@ -1021,11 +1022,11 @@ msgstr "Autores en esta Biblioteca"
#: cps/templates/stats.html:16
msgid "Categories in this Library"
msgstr ""
msgstr "
Categorías en esta librería
"
#: cps/templates/stats.html:20
msgid "Series in this Library"
msgstr ""
msgstr "
Series en esta librería
"
#: cps/templates/stats.html:24
msgid "Linked libraries"
...
...
@@ -1061,7 +1062,7 @@ msgstr "Mostrar libros populares"
#: cps/templates/user_edit.html:55
msgid "Show best rated books"
msgstr ""
msgstr "
Mostrar libros mejor valorados
"
#: cps/templates/user_edit.html:59
msgid "Show language selection"
...
...
@@ -1077,15 +1078,15 @@ msgstr "Mostrar categorias elegidas"
#: cps/templates/user_edit.html:71
msgid "Show author selection"
msgstr ""
msgstr "
Mostrar selección de autores
"
#: cps/templates/user_edit.html:75
msgid "Show read and unread"
msgstr ""
msgstr "
Mostrar leídos y no leídos
"
#: cps/templates/user_edit.html:79
msgid "Show random books in detail view"
msgstr ""
msgstr "
Mostrar libro aleatorios con vista detallada
"
#: cps/templates/user_edit.html:112
msgid "Delete this user"
...
...
@@ -31922,4 +31923,3 @@ msgstr "Zaza"
#. name for zzj
msgid "Zhuang; Zuojiang"
msgstr "Chuang zuojiang"
cps/translations/zh_Hans_CN/LC_MESSAGES/messages.mo
View file @
ccf56302
No preview for this file type
cps/translations/zh_Hans_CN/LC_MESSAGES/messages.po
View file @
ccf56302
...
...
@@ -307,7 +307,7 @@ msgstr "用户 '%(user)s' 已被创建"
#: cps/web.py:2198
msgid "Found an existing account for this email address or nickname."
msgstr "已
找到
使用此邮箱或昵称的账号。"
msgstr "已
存在
使用此邮箱或昵称的账号。"
#: cps/web.py:2220
msgid "Mail settings updated"
...
...
@@ -496,7 +496,7 @@ msgstr "最新提交时间戳"
#: cps/templates/admin.html:83
msgid "Reconnect to Calibre DB"
msgstr ""
msgstr "
重新连接到Calibre数据库
"
#: cps/templates/admin.html:84
msgid "Restart Calibre-web"
...
...
@@ -590,7 +590,7 @@ msgstr "编辑后查看书籍"
#: cps/templates/book_edit.html:107 cps/templates/book_edit.html:118
msgid "Get metadata"
msgstr ""
msgstr "
获取元数据
"
#: cps/templates/book_edit.html:108 cps/templates/config_edit.html:117
#: cps/templates/login.html:19 cps/templates/search_form.html:79
...
...
@@ -600,11 +600,11 @@ msgstr "提交"
#: cps/templates/book_edit.html:121
msgid "Keyword"
msgstr ""
msgstr "
关键字
"
#: cps/templates/book_edit.html:122
msgid " Search keyword "
msgstr ""
msgstr "
搜索关键字
"
#: cps/templates/book_edit.html:124 cps/templates/layout.html:60
msgid "Go!"
...
...
@@ -612,32 +612,32 @@ msgstr "走起!"
#: cps/templates/book_edit.html:125
msgid "Click the cover to load metadata to the form"
msgstr ""
msgstr "
点击封面加载元数据到表单
"
#: cps/templates/book_edit.html:129 cps/templates/book_edit.html:142
msgid "Loading..."
msgstr ""
msgstr "
加载中...
"
#: cps/templates/book_edit.html:132
msgid "Close"
msgstr ""
msgstr "
关闭
"
#: cps/templates/book_edit.html:143
msgid "Search error!"
msgstr ""
msgstr "
搜索错误
"
#: cps/templates/book_edit.html:144
msgid "No Result! Please try anonther keyword."
msgstr ""
msgstr "
没有结果!请尝试别的关键字.
"
#: cps/templates/book_edit.html:146 cps/templates/detail.html:76
#: cps/templates/search_form.html:14
msgid "Publisher"
msgstr ""
msgstr "
出版社
"
#: cps/templates/book_edit.html:148
msgid "Source"
msgstr ""
msgstr "
来源
"
#: cps/templates/config_edit.html:7
msgid "Location of Calibre database"
...
...
@@ -645,7 +645,7 @@ msgstr "Calibre 数据库位置"
#: cps/templates/config_edit.html:13
msgid "Use google drive?"
msgstr ""
msgstr "
是否使用google drive?
"
#: cps/templates/config_edit.html:17
msgid "Client id"
...
...
@@ -660,8 +660,8 @@ msgid "Calibre Base URL"
msgstr ""
#: cps/templates/config_edit.html:29
msgid "Google drive Calibre folde
r
"
msgstr ""
msgid "Google drive Calibre folde"
msgstr "
Google drive Calibre 文件夹
"
#: cps/templates/config_edit.html:38
msgid "Metadata Watch Channel ID"
...
...
@@ -843,12 +843,12 @@ msgstr "显示随机书籍"
#: cps/templates/index.xml:43 cps/templates/index.xml:47
#: cps/templates/layout.html:132
msgid "Read Books"
msgstr ""
msgstr "
已读书籍
"
#: cps/templates/index.xml:50 cps/templates/index.xml:54
#: cps/templates/layout.html:133
msgid "Unread Books"
msgstr ""
msgstr "
未读书籍
"
#: cps/templates/index.xml:57 cps/templates/layout.html:144
msgid "Authors"
...
...
@@ -1082,7 +1082,7 @@ msgstr "显示作者选择"
#: cps/templates/user_edit.html:75
msgid "Show read and unread"
msgstr ""
msgstr "
显示已读和未读
"
#: cps/templates/user_edit.html:79
msgid "Show random books in detail view"
...
...
cps/ub.py
View file @
ccf56302
...
...
@@ -13,7 +13,7 @@ from flask_babel import gettext as _
import
json
#from builtins import str
dbpath
=
os
.
path
.
join
(
os
.
path
.
normpath
(
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
+
os
.
sep
+
".."
+
os
.
sep
),
"app.db"
)
dbpath
=
os
.
path
.
join
(
os
.
path
.
normpath
(
os
.
getenv
(
"CALIBRE_DBPATH"
,
os
.
path
.
dirname
(
os
.
path
.
realpath
(
__file__
))
+
os
.
sep
+
".."
+
os
.
sep
)
),
"app.db"
)
engine
=
create_engine
(
'sqlite:///{0}'
.
format
(
dbpath
),
echo
=
False
)
Base
=
declarative_base
()
...
...
@@ -64,10 +64,7 @@ class UserBase:
return
False
def
role_upload
(
self
):
if
self
.
role
is
not
None
:
return
True
if
self
.
role
&
ROLE_UPLOAD
==
ROLE_UPLOAD
else
False
else
:
return
False
return
bool
((
self
.
role
is
not
None
)
and
(
self
.
role
&
ROLE_UPLOAD
==
ROLE_UPLOAD
))
def
role_edit
(
self
):
if
self
.
role
is
not
None
:
...
...
@@ -93,9 +90,11 @@ class UserBase:
else
:
return
False
@
classmethod
def
is_active
(
self
):
return
True
@
classmethod
def
is_anonymous
(
self
):
return
False
...
...
@@ -106,58 +105,31 @@ class UserBase:
return
self
.
default_language
def
show_random_books
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_RANDOM
==
SIDEBAR_RANDOM
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_RANDOM
==
SIDEBAR_RANDOM
))
def
show_language
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_LANGUAGE
==
SIDEBAR_LANGUAGE
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_LANGUAGE
==
SIDEBAR_LANGUAGE
))
def
show_hot_books
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_HOT
==
SIDEBAR_HOT
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_HOT
==
SIDEBAR_HOT
))
def
show_series
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_SERIES
==
SIDEBAR_SERIES
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_SERIES
==
SIDEBAR_SERIES
))
def
show_category
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_CATEGORY
==
SIDEBAR_CATEGORY
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_CATEGORY
==
SIDEBAR_CATEGORY
))
def
show_author
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_AUTHOR
==
SIDEBAR_AUTHOR
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_AUTHOR
==
SIDEBAR_AUTHOR
))
def
show_best_rated_books
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_BEST_RATED
==
SIDEBAR_BEST_RATED
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_BEST_RATED
==
SIDEBAR_BEST_RATED
))
def
show_read_and_unread
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
SIDEBAR_READ_AND_UNREAD
==
SIDEBAR_READ_AND_UNREAD
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
SIDEBAR_READ_AND_UNREAD
==
SIDEBAR_READ_AND_UNREAD
))
def
show_detail_random
(
self
):
if
self
.
sidebar_view
is
not
None
:
return
True
if
self
.
sidebar_view
&
DETAIL_RANDOM
==
DETAIL_RANDOM
else
False
else
:
return
False
return
bool
((
self
.
sidebar_view
is
not
None
)
and
(
self
.
sidebar_view
&
DETAIL_RANDOM
==
DETAIL_RANDOM
))
def
__repr__
(
self
):
return
'<User
%
r>'
%
self
.
nickname
...
...
@@ -321,10 +293,8 @@ class Config:
else
:
self
.
config_google_drive_watch_changes_response
=
None
self
.
config_columns_to_ignore
=
data
.
config_columns_to_ignore
if
self
.
config_calibre_dir
is
not
None
and
(
not
self
.
config_use_google_drive
or
os
.
path
.
exists
(
self
.
config_calibre_dir
+
'/metadata.db'
)):
self
.
db_configured
=
True
else
:
self
.
db_configured
=
False
self
.
db_configured
=
bool
(
self
.
config_calibre_dir
is
not
None
and
(
not
self
.
config_use_google_drive
or
os
.
path
.
exists
(
self
.
config_calibre_dir
+
'/metadata.db'
)))
@
property
def
get_main_dir
(
self
):
...
...
@@ -506,9 +476,8 @@ def create_anonymous_user():
session
.
add
(
user
)
try
:
session
.
commit
()
except
Exception
as
e
:
except
Exception
:
session
.
rollback
()
pass
# Generate User admin with admin123 password, and access to everything
...
...
@@ -525,9 +494,8 @@ def create_admin_user():
session
.
add
(
user
)
try
:
session
.
commit
()
except
Exception
as
e
:
except
Exception
:
session
.
rollback
()
pass
# Open session for database connection
...
...
cps/web.py
View file @
ccf56302
...
...
@@ -32,6 +32,7 @@ from flask_babel import gettext as _
import
requests
import
zipfile
from
werkzeug.security
import
generate_password_hash
,
check_password_hash
from
werkzeug.datastructures
import
Headers
from
babel
import
Locale
as
LC
from
babel
import
negotiate_locale
from
babel
import
__version__
as
babelVersion
...
...
@@ -40,7 +41,6 @@ from functools import wraps
import
base64
from
sqlalchemy.sql
import
*
import
json
import
urllib
import
datetime
from
iso639
import
languages
as
isoLanguages
from
iso639
import
__version__
as
iso639Version
...
...
@@ -53,24 +53,21 @@ import db
from
shutil
import
move
,
copyfile
from
tornado.ioloop
import
IOLoop
import
shutil
import
StringIO
import
gdriveutils
import
tempfile
import
io
import
hashlib
import
threading
from
tornado
import
version
as
tornadoVersion
try
:
from
urllib.parse
import
quote
from
imp
import
reload
except
ImportError
as
e
:
except
ImportError
:
from
urllib
import
quote
try
:
from
flask_login
import
__version__
as
flask_loginVersion
except
ImportError
as
e
:
except
ImportError
:
from
flask_login.__about__
import
__version__
as
flask_loginVersion
import
time
...
...
@@ -82,7 +79,6 @@ try:
use_generic_pdf_cover
=
False
except
ImportError
:
use_generic_pdf_cover
=
True
from
cgi
import
escape
# Global variables
gdrive_watch_callback_token
=
'target=calibreweb-watch_files'
...
...
@@ -135,6 +131,7 @@ class Singleton:
def
__instancecheck__
(
self
,
inst
):
return
isinstance
(
inst
,
self
.
_decorated
)
@
Singleton
class
Gauth
:
def
__init__
(
self
):
...
...
@@ -275,14 +272,9 @@ def load_user_from_header(header_val):
return
user
return
def
check_auth
(
username
,
password
):
user
=
ub
.
session
.
query
(
ub
.
User
)
.
filter
(
ub
.
User
.
nickname
==
username
)
.
first
()
if
user
and
check_password_hash
(
user
.
password
,
password
):
return
True
else
:
return
False
return
bool
(
user
and
check_password_hash
(
user
.
password
,
password
))
def
authenticate
():
return
Response
(
...
...
@@ -389,7 +381,7 @@ def shortentitle_filter(s):
def
mimetype_filter
(
val
):
try
:
s
=
mimetypes
.
types_map
[
'.'
+
val
]
except
Exception
as
e
:
except
Exception
:
s
=
'application/octet-stream'
return
s
...
...
@@ -406,10 +398,10 @@ def timestamptodate(date, fmt=None):
)
native
=
date
.
replace
(
tzinfo
=
None
)
if
fmt
:
format
=
fmt
time_
format
=
fmt
else
:
format
=
'
%
d
%
m
%
Y -
%
H:
%
S'
return
native
.
strftime
(
format
)
time_
format
=
'
%
d
%
m
%
Y -
%
H:
%
S'
return
native
.
strftime
(
time_
format
)
def
admin_required
(
f
):
"""
...
...
@@ -472,22 +464,22 @@ def edit_required(f):
# Fill indexpage with all requested data from database
def
fill_indexpage
(
page
,
database
,
db_filter
,
order
):
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
if
current_user
.
show_detail_random
():
random
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_random_books
)
random
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_random_books
)
else
:
random
=
false
off
=
int
(
int
(
config
.
config_books_per_page
)
*
(
page
-
1
))
pagination
=
Pagination
(
page
,
config
.
config_books_per_page
,
len
(
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
.
filter
(
filter
)
.
all
()))
entries
=
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
.
filter
(
filter
)
.
order_by
(
order
)
.
offset
(
off
)
.
limit
(
len
(
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
.
filter
(
lang_
filter
)
.
all
()))
entries
=
db
.
session
.
query
(
database
)
.
filter
(
db_filter
)
.
filter
(
lang_
filter
)
.
order_by
(
order
)
.
offset
(
off
)
.
limit
(
config
.
config_books_per_page
)
return
entries
,
random
,
pagination
def
modify_database_object
(
input_elements
,
db_book_object
,
db_object
,
db_session
,
type
):
def
modify_database_object
(
input_elements
,
db_book_object
,
db_object
,
db_session
,
db_
type
):
input_elements
=
[
x
for
x
in
input_elements
if
x
!=
''
]
# we have all input element (authors, series, tags) names now
# 1. search for elements to remove
...
...
@@ -519,7 +511,7 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
db_session
.
delete
(
del_element
)
# if there are elements to add, we add them now!
if
len
(
add_elements
)
>
0
:
if
type
==
'languages'
:
if
db_
type
==
'languages'
:
db_filter
=
db_object
.
lang_code
else
:
db_filter
=
db_object
.
name
...
...
@@ -528,12 +520,12 @@ def modify_database_object(input_elements, db_book_object, db_object, db_session
new_element
=
db_session
.
query
(
db_object
)
.
filter
(
db_filter
==
add_element
)
.
first
()
# if no element is found add it
if
new_element
is
None
:
if
type
==
'author'
:
if
db_
type
==
'author'
:
new_element
=
db_object
(
add_element
,
add_element
,
""
)
else
:
if
type
==
'series'
:
if
db_
type
==
'series'
:
new_element
=
db_object
(
add_element
,
add_element
)
else
:
# type should be tag, or languages
else
:
#
db_
type should be tag, or languages
new_element
=
db_object
(
add_element
)
db_session
.
add
(
new_element
)
new_element
=
db
.
session
.
query
(
db_object
)
.
filter
(
db_filter
==
add_element
)
.
first
()
...
...
@@ -562,10 +554,6 @@ def before_request():
@
app
.
route
(
"/opds"
)
@
requires_basic_auth_if_no_ano
def
feed_index
():
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
xml
=
render_title_template
(
'index.xml'
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/xml"
...
...
@@ -575,7 +563,7 @@ def feed_index():
@
app
.
route
(
"/opds/osd"
)
@
requires_basic_auth_if_no_ano
def
feed_osd
():
xml
=
render_title_template
(
'osd.xml'
,
lang
=
'de-DE'
)
xml
=
render_title_template
(
'osd.xml'
,
lang
=
'de-DE'
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/xml"
return
response
...
...
@@ -595,15 +583,15 @@ def feed_normal_search():
def
feed_search
(
term
):
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
if
term
:
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
or_
(
db
.
Books
.
tags
.
any
(
db
.
Tags
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
series
.
any
(
db
.
Series
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
authors
.
any
(
db
.
Authors
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
publishers
.
any
(
db
.
Publishers
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
title
.
like
(
"
%
"
+
term
+
"
%
"
)))
.
filter
(
filter
)
.
all
()
db
.
Books
.
title
.
like
(
"
%
"
+
term
+
"
%
"
)))
.
filter
(
lang_
filter
)
.
all
()
entriescount
=
len
(
entries
)
if
len
(
entries
)
>
0
else
1
pagination
=
Pagination
(
1
,
entriescount
,
entriescount
)
xml
=
render_title_template
(
'feed.xml'
,
searchterm
=
term
,
entries
=
entries
,
pagination
=
pagination
)
...
...
@@ -620,7 +608,7 @@ def feed_new():
off
=
request
.
args
.
get
(
"offset"
)
if
not
off
:
off
=
0
entries
,
random
,
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
())
xml
=
render_title_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
response
=
make_response
(
xml
)
...
...
@@ -632,10 +620,10 @@ def feed_new():
@
requires_basic_auth_if_no_ano
def
feed_discover
():
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_books_per_page
)
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_books_per_page
)
pagination
=
Pagination
(
1
,
config
.
config_books_per_page
,
int
(
config
.
config_books_per_page
))
xml
=
render_title_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
response
=
make_response
(
xml
)
...
...
@@ -663,9 +651,9 @@ def feed_hot():
if
not
off
:
off
=
0
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
all_books
=
ub
.
session
.
query
(
ub
.
Downloads
,
ub
.
func
.
count
(
ub
.
Downloads
.
book_id
))
.
order_by
(
ub
.
func
.
count
(
ub
.
Downloads
.
book_id
)
.
desc
())
.
group_by
(
ub
.
Downloads
.
book_id
)
hot_books
=
all_books
.
offset
(
off
)
.
limit
(
config
.
config_books_per_page
)
...
...
@@ -674,7 +662,7 @@ def feed_hot():
downloadBook
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
()
if
downloadBook
:
entries
.
append
(
db
.
session
.
query
(
db
.
Books
)
.
filter
(
filter
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
())
db
.
session
.
query
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
())
else
:
ub
.
session
.
query
(
ub
.
Downloads
)
.
filter
(
book
.
Downloads
.
book_id
==
ub
.
Downloads
.
book_id
)
.
delete
()
ub
.
session
.
commit
()
...
...
@@ -693,10 +681,10 @@ def feed_authorindex():
if
not
off
:
off
=
0
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Authors
)
.
join
(
db
.
books_authors_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
\
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Authors
)
.
join
(
db
.
books_authors_link
)
.
join
(
db
.
Books
)
.
filter
(
lang_
filter
)
\
.
group_by
(
'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
,
len
(
db
.
session
.
query
(
db
.
Authors
)
.
all
()))
...
...
@@ -706,14 +694,14 @@ def feed_authorindex():
return
response
@
app
.
route
(
"/opds/author/<int:id>"
)
@
app
.
route
(
"/opds/author/<int:
book_
id>"
)
@
requires_basic_auth_if_no_ano
def
feed_author
(
id
):
def
feed_author
(
book_
id
):
off
=
request
.
args
.
get
(
"offset"
)
if
not
off
:
off
=
0
entries
,
random
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
id
),
db
.
Books
.
timestamp
.
desc
())
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
book_
id
),
db
.
Books
.
timestamp
.
desc
())
xml
=
render_title_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/xml"
...
...
@@ -727,10 +715,10 @@ def feed_categoryindex():
if
not
off
:
off
=
0
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Tags
)
.
join
(
db
.
books_tags_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
.
\
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Tags
)
.
join
(
db
.
books_tags_link
)
.
join
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
\
group_by
(
'books_tags_link.tag'
)
.
order_by
(
db
.
Tags
.
name
)
.
offset
(
off
)
.
limit
(
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
.
Tags
)
.
all
()))
...
...
@@ -740,14 +728,14 @@ def feed_categoryindex():
return
response
@
app
.
route
(
"/opds/category/<int:id>"
)
@
app
.
route
(
"/opds/category/<int:
book_
id>"
)
@
requires_basic_auth_if_no_ano
def
feed_category
(
id
):
def
feed_category
(
book_
id
):
off
=
request
.
args
.
get
(
"offset"
)
if
not
off
:
off
=
0
entries
,
random
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
id
),
db
.
Books
.
timestamp
.
desc
())
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
book_
id
),
db
.
Books
.
timestamp
.
desc
())
xml
=
render_title_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/xml"
...
...
@@ -761,10 +749,10 @@ def feed_seriesindex():
if
not
off
:
off
=
0
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Series
)
.
join
(
db
.
books_series_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
.
\
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Series
)
.
join
(
db
.
books_series_link
)
.
join
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
\
group_by
(
'books_series_link.series'
)
.
order_by
(
db
.
Series
.
sort
)
.
offset
(
off
)
.
all
()
pagination
=
Pagination
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
config
.
config_books_per_page
,
len
(
db
.
session
.
query
(
db
.
Series
)
.
all
()))
...
...
@@ -774,14 +762,14 @@ def feed_seriesindex():
return
response
@
app
.
route
(
"/opds/series/<int:id>"
)
@
app
.
route
(
"/opds/series/<int:
book_
id>"
)
@
requires_basic_auth_if_no_ano
def
feed_series
(
id
):
def
feed_series
(
book_
id
):
off
=
request
.
args
.
get
(
"offset"
)
if
not
off
:
off
=
0
entries
,
random
,
pagination
=
fill_indexpage
((
int
(
off
)
/
(
int
(
config
.
config_books_per_page
))
+
1
),
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
id
),
db
.
Books
.
series_index
)
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
book_
id
),
db
.
Books
.
series_index
)
xml
=
render_title_template
(
'feed.xml'
,
entries
=
entries
,
pagination
=
pagination
)
response
=
make_response
(
xml
)
response
.
headers
[
"Content-Type"
]
=
"application/xml"
...
...
@@ -796,13 +784,12 @@ def partial(total_byte_len, part_size_limit):
return
s
def
do_gdrive_download
(
df
,
headers
):
startTime
=
time
.
time
()
total_size
=
int
(
df
.
metadata
.
get
(
'fileSize'
))
download_url
=
df
.
metadata
.
get
(
'downloadUrl'
)
s
=
partial
(
total_size
,
1024
*
1024
)
# I'm downloading BIG files, so 100M chunk size is fine for me
def
stream
():
for
byte
s
in
s
:
headers
=
{
"Range"
:
'bytes=
%
s-
%
s'
%
(
byte
s
[
0
],
bytes
[
1
])}
for
byte
in
s
:
headers
=
{
"Range"
:
'bytes=
%
s-
%
s'
%
(
byte
[
0
],
byte
[
1
])}
resp
,
content
=
df
.
auth
.
Get_Http_Object
()
.
request
(
download_url
,
headers
=
headers
)
if
resp
.
status
==
206
:
yield
content
...
...
@@ -811,14 +798,14 @@ def do_gdrive_download(df, headers):
return
return
Response
(
stream_with_context
(
stream
()),
headers
=
headers
)
@
app
.
route
(
"/opds/download/<book_id>/<format>/"
)
@
app
.
route
(
"/opds/download/<book_id>/<
book_
format>/"
)
@
requires_basic_auth_if_no_ano
@
download_required
def
get_opds_download_link
(
book_id
,
format
):
def
get_opds_download_link
(
book_id
,
book_
format
):
startTime
=
time
.
time
()
format
=
format
.
split
(
"."
)[
0
]
book_format
=
book_
format
.
split
(
"."
)[
0
]
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
data
=
db
.
session
.
query
(
db
.
Data
)
.
filter
(
db
.
Data
.
book
==
book
.
id
)
.
filter
(
db
.
Data
.
format
==
format
.
upper
())
.
first
()
data
=
db
.
session
.
query
(
db
.
Data
)
.
filter
(
db
.
Data
.
book
==
book
.
id
)
.
filter
(
db
.
Data
.
format
==
book_
format
.
upper
())
.
first
()
app
.
logger
.
info
(
data
.
name
)
if
current_user
.
is_authenticated
:
helper
.
update_download
(
book_id
,
int
(
current_user
.
id
))
...
...
@@ -826,16 +813,16 @@ def get_opds_download_link(book_id, format):
if
len
(
book
.
authors
)
>
0
:
file_name
=
book
.
authors
[
0
]
.
name
+
'_'
+
file_name
file_name
=
helper
.
get_valid_filename
(
file_name
)
headers
=
{}
headers
[
"Content-Disposition"
]
=
"attachment; filename*=UTF-8''
%
s.
%
s"
%
(
quote
(
file_name
.
encode
(
'utf8'
)),
format
)
headers
=
Headers
()
headers
[
"Content-Disposition"
]
=
"attachment; filename*=UTF-8''
%
s.
%
s"
%
(
quote
(
file_name
.
encode
(
'utf8'
)),
book_
format
)
app
.
logger
.
info
(
time
.
time
()
-
startTime
)
startTime
=
time
.
time
()
if
config
.
config_use_google_drive
:
df
=
gdriveutils
.
getFileFromEbooksFolder
(
Gdrive
.
Instance
()
.
drive
,
book
.
path
,
data
.
name
+
"."
+
format
)
app
.
logger
.
info
(
time
.
time
()
-
startTime
)
df
=
gdriveutils
.
getFileFromEbooksFolder
(
Gdrive
.
Instance
()
.
drive
,
book
.
path
,
data
.
name
+
"."
+
book_format
)
return
do_gdrive_download
(
df
,
headers
)
else
:
# file_name = helper.get_valid_filename(file_name)
response
=
make_response
(
send_from_directory
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
),
data
.
name
+
"."
+
format
))
response
=
make_response
(
send_from_directory
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
),
data
.
name
+
"."
+
book_format
))
response
.
headers
=
headers
return
response
...
...
@@ -858,8 +845,9 @@ def get_metadata_calibre_companion(uuid):
def
get_authors_json
():
if
request
.
method
==
"GET"
:
query
=
request
.
args
.
get
(
'q'
)
entries
=
db
.
session
.
execute
(
"select name from authors where name like '
%
"
+
query
+
"
%
'"
)
json_dumps
=
json
.
dumps
([
dict
(
r
)
for
r
in
entries
])
# entries = db.session.execute("select name from authors where name like '%" + query + "%'")
entries
=
db
.
session
.
query
(
db
.
Authors
)
.
filter
(
db
.
Authors
.
name
.
like
(
"
%
"
+
query
+
"
%
"
))
.
all
()
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
)
for
r
in
entries
])
return
json_dumps
...
...
@@ -868,8 +856,11 @@ def get_authors_json():
def
get_tags_json
():
if
request
.
method
==
"GET"
:
query
=
request
.
args
.
get
(
'q'
)
entries
=
db
.
session
.
execute
(
"select name from tags where name like '
%
"
+
query
+
"
%
'"
)
json_dumps
=
json
.
dumps
([
dict
(
r
)
for
r
in
entries
])
# entries = db.session.execute("select name from tags where name like '%" + query + "%'")
entries
=
db
.
session
.
query
(
db
.
Tags
)
.
filter
(
db
.
Tags
.
name
.
like
(
"
%
"
+
query
+
"
%
"
))
.
all
()
#for x in entries:
# alfa = dict(name=x.name)
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
)
for
r
in
entries
])
return
json_dumps
@
app
.
route
(
"/get_update_status"
,
methods
=
[
'GET'
])
...
...
@@ -915,7 +906,7 @@ def get_updater_status():
elif
request
.
method
==
"GET"
:
try
:
status
[
'status'
]
=
helper
.
updater_thread
.
get_update_status
()
except
Exception
as
e
:
except
Exception
:
status
[
'status'
]
=
7
return
json
.
dumps
(
status
)
...
...
@@ -930,7 +921,7 @@ def get_languages_json():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
entries
=
[
s
for
s
in
languages
if
query
in
s
.
name
.
lower
()]
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
)
for
r
in
entries
])
...
...
@@ -942,8 +933,9 @@ def get_languages_json():
def
get_series_json
():
if
request
.
method
==
"GET"
:
query
=
request
.
args
.
get
(
'q'
)
entries
=
db
.
session
.
execute
(
"select name from series where name like '
%
"
+
query
+
"
%
'"
)
json_dumps
=
json
.
dumps
([
dict
(
r
)
for
r
in
entries
])
entries
=
db
.
session
.
query
(
db
.
Series
)
.
filter
(
db
.
Series
.
name
.
like
(
"
%
"
+
query
+
"
%
"
))
.
all
()
# entries = db.session.execute("select name from series where name like '%" + query + "%'")
json_dumps
=
json
.
dumps
([
dict
(
name
=
r
.
name
)
for
r
in
entries
])
return
json_dumps
...
...
@@ -987,11 +979,11 @@ def index(page):
@
login_required_if_no_ano
def
hot_books
(
page
):
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
if
current_user
.
show_detail_random
():
random
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_random_books
)
random
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_random_books
)
else
:
random
=
false
off
=
int
(
int
(
config
.
config_books_per_page
)
*
(
page
-
1
))
...
...
@@ -1003,7 +995,7 @@ def hot_books(page):
downloadBook
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
()
if
downloadBook
:
entries
.
append
(
db
.
session
.
query
(
db
.
Books
)
.
filter
(
filter
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
())
db
.
session
.
query
(
db
.
Books
)
.
filter
(
lang_
filter
)
.
filter
(
db
.
Books
.
id
==
book
.
Downloads
.
book_id
)
.
first
())
else
:
ub
.
session
.
query
(
ub
.
Downloads
)
.
filter
(
book
.
Downloads
.
book_id
==
ub
.
Downloads
.
book_id
)
.
delete
()
ub
.
session
.
commit
()
...
...
@@ -1036,22 +1028,22 @@ def discover(page):
@
login_required_if_no_ano
def
author_list
():
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Authors
,
func
.
count
(
'books_authors_link.book'
)
.
label
(
'count'
))
.
join
(
db
.
books_authors_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
.
group_by
(
'books_authors_link.author'
)
.
order_by
(
db
.
Authors
.
sort
)
.
all
()
lang_
filter
)
.
group_by
(
'books_authors_link.author'
)
.
order_by
(
db
.
Authors
.
sort
)
.
all
()
return
render_title_template
(
'list.html'
,
entries
=
entries
,
folder
=
'author'
,
title
=
_
(
u"Author list"
))
@
app
.
route
(
"/author/<int:id>"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
"/author/<int:id>/<int:page>'"
)
@
app
.
route
(
"/author/<int:
book_
id>"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
"/author/<int:
book_
id>/<int:page>'"
)
@
login_required_if_no_ano
def
author
(
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
id
),
def
author
(
book_
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
authors
.
any
(
db
.
Authors
.
id
==
book_
id
),
db
.
Books
.
timestamp
.
desc
())
name
=
db
.
session
.
query
(
db
.
Authors
)
.
filter
(
db
.
Authors
.
id
==
id
)
.
first
()
.
name
name
=
db
.
session
.
query
(
db
.
Authors
)
.
filter
(
db
.
Authors
.
id
==
book_
id
)
.
first
()
.
name
if
entries
:
return
render_title_template
(
'index.html'
,
random
=
random
,
entries
=
entries
,
title
=
_
(
u"Author:
%(name)
s"
,
name
=
name
))
else
:
...
...
@@ -1063,22 +1055,22 @@ def author(id,page):
@
login_required_if_no_ano
def
series_list
():
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Series
,
func
.
count
(
'books_series_link.book'
)
.
label
(
'count'
))
.
join
(
db
.
books_series_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
.
group_by
(
'books_series_link.series'
)
.
order_by
(
db
.
Series
.
sort
)
.
all
()
lang_
filter
)
.
group_by
(
'books_series_link.series'
)
.
order_by
(
db
.
Series
.
sort
)
.
all
()
return
render_title_template
(
'list.html'
,
entries
=
entries
,
folder
=
'series'
,
title
=
_
(
u"Series list"
))
@
app
.
route
(
"/series/<int:id>/"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
"/series/<int:id>/<int:page>'"
)
@
app
.
route
(
"/series/<int:
book_
id>/"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
"/series/<int:
book_
id>/<int:page>'"
)
@
login_required_if_no_ano
def
series
(
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
id
),
def
series
(
book_
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
series
.
any
(
db
.
Series
.
id
==
book_
id
),
db
.
Books
.
series_index
)
name
=
db
.
session
.
query
(
db
.
Series
)
.
filter
(
db
.
Series
.
id
==
id
)
.
first
()
.
name
name
=
db
.
session
.
query
(
db
.
Series
)
.
filter
(
db
.
Series
.
id
==
book_
id
)
.
first
()
.
name
if
entries
:
return
render_title_template
(
'index.html'
,
random
=
random
,
pagination
=
pagination
,
entries
=
entries
,
title
=
_
(
u"Series:
%(serie)
s"
,
serie
=
name
))
...
...
@@ -1096,13 +1088,13 @@ def language_overview():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
else
:
try
:
langfound
=
1
cur_l
=
LC
.
parse
(
current_user
.
filter_language
())
except
Exception
as
e
:
except
Exception
:
langfound
=
0
languages
=
db
.
session
.
query
(
db
.
Languages
)
.
filter
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
.
all
()
...
...
@@ -1126,7 +1118,7 @@ def language(name, page):
try
:
cur_l
=
LC
.
parse
(
name
)
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
name
=
_
(
isoLanguages
.
get
(
part3
=
name
)
.
name
)
return
render_title_template
(
'index.html'
,
random
=
random
,
entries
=
entries
,
pagination
=
pagination
,
title
=
_
(
u"Language:
%(name)
s"
,
name
=
name
))
...
...
@@ -1136,58 +1128,58 @@ def language(name, page):
@
login_required_if_no_ano
def
category_list
():
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Tags
,
func
.
count
(
'books_tags_link.book'
)
.
label
(
'count'
))
.
join
(
db
.
books_tags_link
)
.
join
(
db
.
Books
)
.
filter
(
filter
)
.
group_by
(
'books_tags_link.tag'
)
.
all
()
lang_
filter
)
.
group_by
(
'books_tags_link.tag'
)
.
all
()
return
render_title_template
(
'list.html'
,
entries
=
entries
,
folder
=
'category'
,
title
=
_
(
u"Category list"
))
@
app
.
route
(
"/category/<int:id>"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
'/category/<int:id>/<int:page>'
)
@
app
.
route
(
"/category/<int:
book_
id>"
,
defaults
=
{
'page'
:
1
})
@
app
.
route
(
'/category/<int:
book_
id>/<int:page>'
)
@
login_required_if_no_ano
def
category
(
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
id
),
def
category
(
book_
id
,
page
):
entries
,
random
,
pagination
=
fill_indexpage
(
page
,
db
.
Books
,
db
.
Books
.
tags
.
any
(
db
.
Tags
.
id
==
book_
id
),
db
.
Books
.
timestamp
.
desc
())
name
=
db
.
session
.
query
(
db
.
Tags
)
.
filter
(
db
.
Tags
.
id
==
id
)
.
first
()
.
name
name
=
db
.
session
.
query
(
db
.
Tags
)
.
filter
(
db
.
Tags
.
id
==
book_
id
)
.
first
()
.
name
return
render_title_template
(
'index.html'
,
random
=
random
,
entries
=
entries
,
pagination
=
pagination
,
title
=
_
(
u"Category:
%(name)
s"
,
name
=
name
))
@
app
.
route
(
"/ajax/toggleread/<int:id>"
,
methods
=
[
'POST'
])
@
app
.
route
(
"/ajax/toggleread/<int:
book_
id>"
,
methods
=
[
'POST'
])
@
login_required
def
toggle_read
(
id
):
def
toggle_read
(
book_
id
):
book
=
ub
.
session
.
query
(
ub
.
ReadBook
)
.
filter
(
ub
.
and_
(
ub
.
ReadBook
.
user_id
==
int
(
current_user
.
id
),
ub
.
ReadBook
.
book_id
==
id
))
.
first
()
ub
.
ReadBook
.
book_id
==
book_
id
))
.
first
()
if
book
:
book
.
is_read
=
not
book
.
is_read
else
:
readBook
=
ub
.
ReadBook
()
readBook
.
user_id
=
int
(
current_user
.
id
)
readBook
.
book_id
=
id
readBook
.
book_id
=
book_
id
readBook
.
is_read
=
True
book
=
readBook
ub
.
session
.
merge
(
book
)
ub
.
session
.
commit
()
return
""
@
app
.
route
(
"/book/<int:id>"
)
@
app
.
route
(
"/book/<int:
book_
id>"
)
@
login_required_if_no_ano
def
show_book
(
id
):
def
show_book
(
book_
id
):
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
id
)
.
filter
(
filter
)
.
first
()
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
lang_
filter
)
.
first
()
if
entries
:
for
index
in
range
(
0
,
len
(
entries
.
languages
)):
try
:
entries
.
languages
[
index
]
.
language_name
=
LC
.
parse
(
entries
.
languages
[
index
]
.
lang_code
)
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
entries
.
languages
[
index
]
.
language_name
=
_
(
isoLanguages
.
get
(
part3
=
entries
.
languages
[
index
]
.
lang_code
)
.
name
)
tmpcc
=
db
.
session
.
query
(
db
.
Custom_Columns
)
.
filter
(
db
.
Custom_Columns
.
datatype
.
notin_
(
db
.
cc_exceptions
))
.
all
()
...
...
@@ -1201,7 +1193,7 @@ def show_book(id):
else
:
cc
=
tmpcc
book_in_shelfs
=
[]
shelfs
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
book_id
==
id
)
.
all
()
shelfs
=
ub
.
session
.
query
(
ub
.
BookShelf
)
.
filter
(
ub
.
BookShelf
.
book_id
==
book_
id
)
.
all
()
for
entry
in
shelfs
:
book_in_shelfs
.
append
(
entry
.
shelf
)
...
...
@@ -1209,7 +1201,7 @@ def show_book(id):
# title=entries.title, books_shelfs=book_in_shelfs)
if
not
current_user
.
is_anonymous
():
matching_have_read_book
=
ub
.
session
.
query
(
ub
.
ReadBook
)
.
filter
(
ub
.
and_
(
ub
.
ReadBook
.
user_id
==
int
(
current_user
.
id
),
ub
.
ReadBook
.
book_id
==
id
))
.
all
()
ub
.
ReadBook
.
book_id
==
book_
id
))
.
all
()
have_read
=
len
(
matching_have_read_book
)
>
0
and
matching_have_read_book
[
0
]
.
is_read
else
:
have_read
=
None
...
...
@@ -1242,11 +1234,10 @@ def stats():
kindlegen
=
os
.
path
.
join
(
vendorpath
,
u"kindlegen"
)
versions
[
'KindlegenVersion'
]
=
_
(
'not installed'
)
if
os
.
path
.
exists
(
kindlegen
):
p
=
subprocess
.
Popen
(
kindlegen
,
shell
=
True
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
,
stdin
=
subprocess
.
PIPE
)
p
=
subprocess
.
Popen
(
kindlegen
,
stdout
=
subprocess
.
PIPE
,
stderr
=
subprocess
.
PIPE
)
p
.
wait
()
for
lines
in
p
.
stdout
.
readlines
():
if
type
(
lines
)
is
bytes
:
if
isinstance
(
lines
,
bytes
)
:
lines
=
lines
.
decode
(
'utf-8'
)
if
re
.
search
(
'Amazon kindlegen
\
('
,
lines
):
versions
[
'KindlegenVersion'
]
=
lines
...
...
@@ -1308,7 +1299,7 @@ def revoke_watch_gdrive():
last_watch_response
=
config
.
config_google_drive_watch_changes_response
if
last_watch_response
:
try
:
response
=
gdriveutils
.
stopChannel
(
Gdrive
.
Instance
()
.
drive
,
last_watch_response
[
'id'
],
last_watch_response
[
'resourceId'
])
gdriveutils
.
stopChannel
(
Gdrive
.
Instance
()
.
drive
,
last_watch_response
[
'id'
],
last_watch_response
[
'resourceId'
])
except
HttpError
:
pass
settings
=
ub
.
session
.
query
(
ub
.
Settings
)
.
first
()
...
...
@@ -1340,13 +1331,13 @@ def on_received_watch_confirmation():
if
not
response
[
'deleted'
]
and
response
[
'file'
][
'title'
]
==
'metadata.db'
and
response
[
'file'
][
'md5Checksum'
]
!=
md5
(
dbpath
):
tmpDir
=
tempfile
.
gettempdir
()
app
.
logger
.
info
(
'Database file updated'
)
copyfile
(
dbpath
,
tmpDir
+
"/metadata.db_"
+
str
(
current_milli_time
(
)))
copyfile
(
dbpath
,
os
.
path
.
join
(
tmpDir
,
"metadata.db_"
+
str
(
current_milli_time
()
)))
app
.
logger
.
info
(
'Backing up existing and downloading updated metadata.db'
)
gdriveutils
.
downloadFile
(
Gdrive
.
Instance
()
.
drive
,
None
,
"metadata.db"
,
tmpDir
+
"/tmp_metadata.db"
)
gdriveutils
.
downloadFile
(
Gdrive
.
Instance
()
.
drive
,
None
,
"metadata.db"
,
os
.
path
.
join
(
tmpDir
,
"tmp_metadata.db"
)
)
app
.
logger
.
info
(
'Setting up new DB'
)
os
.
rename
(
tmpDir
+
"/tmp_metadata.db"
,
dbpath
)
os
.
rename
(
os
.
path
.
join
(
tmpDir
,
"tmp_metadata.db"
)
,
dbpath
)
db
.
setup_db
()
except
Exception
,
e
:
except
Exception
as
e
:
app
.
logger
.
exception
(
e
)
updateMetaData
()
...
...
@@ -1389,7 +1380,7 @@ def shutdown():
def
update
():
helper
.
updater_thread
=
helper
.
Updater
()
flash
(
_
(
u"Update done"
),
category
=
"info"
)
return
""
return
abort
(
404
)
@
app
.
route
(
"/search"
,
methods
=
[
"GET"
])
...
...
@@ -1398,14 +1389,14 @@ def search():
term
=
request
.
args
.
get
(
"query"
)
.
strip
()
if
term
:
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
lang_
filter
=
True
entries
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
or_
(
db
.
Books
.
tags
.
any
(
db
.
Tags
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
series
.
any
(
db
.
Series
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
authors
.
any
(
db
.
Authors
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
publishers
.
any
(
db
.
Publishers
.
name
.
like
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
title
.
like
(
"
%
"
+
term
+
"
%
"
)))
.
filter
(
filter
)
.
all
()
db
.
Books
.
title
.
like
(
"
%
"
+
term
+
"
%
"
)))
.
filter
(
lang_
filter
)
.
all
()
return
render_title_template
(
'search.html'
,
searchterm
=
term
,
entries
=
entries
)
else
:
return
render_title_template
(
'search.html'
,
searchterm
=
""
)
...
...
@@ -1443,7 +1434,7 @@ def advanced_search():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
searchterm
.
extend
(
language
.
name
for
language
in
language_names
)
searchterm
=
" + "
.
join
(
filter
(
None
,
searchterm
))
...
...
@@ -1475,7 +1466,7 @@ def advanced_search():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
else
:
languages
=
None
...
...
@@ -1485,7 +1476,7 @@ def advanced_search():
def
get_cover_via_gdrive
(
cover_path
):
df
=
gdriveutils
.
getFileFromEbooksFolder
(
Gdrive
.
Instance
()
.
drive
,
cover_path
,
'cover.jpg'
)
if
not
gdriveutils
.
session
.
query
(
gdriveutils
.
PermissionAdded
)
.
filter
(
gdriveutils
.
PermissionAdded
.
gdrive_id
==
df
[
'id'
])
.
first
():
permissions
=
df
.
GetPermissions
()
df
.
GetPermissions
()
df
.
InsertPermission
({
'type'
:
'anyone'
,
'value'
:
'anyone'
,
...
...
@@ -1566,15 +1557,15 @@ def feed_unread_books():
def
unread_books
(
page
):
return
render_read_books
(
page
,
False
)
@
app
.
route
(
"/read/<int:book_id>/<format>"
)
@
app
.
route
(
"/read/<int:book_id>/<
book_
format>"
)
@
login_required_if_no_ano
def
read_book
(
book_id
,
format
):
def
read_book
(
book_id
,
book_
format
):
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
if
book
:
book_dir
=
os
.
path
.
join
(
config
.
get_main_dir
,
"cps"
,
"static"
,
str
(
book_id
))
if
not
os
.
path
.
exists
(
book_dir
):
os
.
mkdir
(
book_dir
)
if
format
.
lower
()
==
"epub"
:
if
book_
format
.
lower
()
==
"epub"
:
# check if mimetype file is exists
mime_file
=
str
(
book_id
)
+
"/mimetype"
if
not
os
.
path
.
exists
(
mime_file
):
...
...
@@ -1589,9 +1580,7 @@ def read_book(book_id, format):
try
:
os
.
makedirs
(
newDir
)
except
OSError
as
exception
:
if
exception
.
errno
==
errno
.
EEXIST
:
pass
else
:
if
not
exception
.
errno
==
errno
.
EEXIST
:
raise
if
fileName
:
fd
=
open
(
os
.
path
.
join
(
newDir
,
fileName
),
"wb"
)
...
...
@@ -1599,21 +1588,21 @@ def read_book(book_id, format):
fd
.
close
()
zfile
.
close
()
return
render_title_template
(
'read.html'
,
bookid
=
book_id
,
title
=
_
(
u"Read a Book"
))
elif
format
.
lower
()
==
"pdf"
:
elif
book_
format
.
lower
()
==
"pdf"
:
all_name
=
str
(
book_id
)
+
"/"
+
book
.
data
[
0
]
.
name
+
".pdf"
tmp_file
=
os
.
path
.
join
(
book_dir
,
book
.
data
[
0
]
.
name
)
+
".pdf"
if
not
os
.
path
.
exists
(
tmp_file
):
pdf_file
=
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
,
book
.
data
[
0
]
.
name
)
+
".pdf"
copyfile
(
pdf_file
,
tmp_file
)
return
render_title_template
(
'readpdf.html'
,
pdffile
=
all_name
,
title
=
_
(
u"Read a Book"
))
elif
format
.
lower
()
==
"txt"
:
elif
book_
format
.
lower
()
==
"txt"
:
all_name
=
str
(
book_id
)
+
"/"
+
book
.
data
[
0
]
.
name
+
".txt"
tmp_file
=
os
.
path
.
join
(
book_dir
,
book
.
data
[
0
]
.
name
)
+
".txt"
if
not
os
.
path
.
exists
(
all_name
):
txt_file
=
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
,
book
.
data
[
0
]
.
name
)
+
".txt"
copyfile
(
txt_file
,
tmp_file
)
return
render_title_template
(
'readtxt.html'
,
txtfile
=
all_name
,
title
=
_
(
u"Read a Book"
))
elif
format
.
lower
()
==
"cbr"
:
elif
book_
format
.
lower
()
==
"cbr"
:
all_name
=
str
(
book_id
)
+
"/"
+
book
.
data
[
0
]
.
name
+
".cbr"
tmp_file
=
os
.
path
.
join
(
book_dir
,
book
.
data
[
0
]
.
name
)
+
".cbr"
if
not
os
.
path
.
exists
(
all_name
):
...
...
@@ -1625,13 +1614,13 @@ def read_book(book_id, format):
flash
(
_
(
u"Error opening eBook. File does not exist or file is not accessible:"
),
category
=
"error"
)
return
redirect
(
url_for
(
"index"
))
@
app
.
route
(
"/download/<int:book_id>/<format>"
)
@
app
.
route
(
"/download/<int:book_id>/<
book_
format>"
)
@
login_required_if_no_ano
@
download_required
def
get_download_link
(
book_id
,
format
):
format
=
format
.
split
(
"."
)[
0
]
def
get_download_link
(
book_id
,
book_
format
):
book_format
=
book_
format
.
split
(
"."
)[
0
]
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
first
()
data
=
db
.
session
.
query
(
db
.
Data
)
.
filter
(
db
.
Data
.
book
==
book
.
id
)
.
filter
(
db
.
Data
.
format
==
format
.
upper
())
.
first
()
data
=
db
.
session
.
query
(
db
.
Data
)
.
filter
(
db
.
Data
.
book
==
book
.
id
)
.
filter
(
db
.
Data
.
format
==
book_
format
.
upper
())
.
first
()
if
data
:
# collect downloaded books only for registered user and not for anonymous user
if
current_user
.
is_authenticated
:
...
...
@@ -1640,27 +1629,27 @@ def get_download_link(book_id, format):
if
len
(
book
.
authors
)
>
0
:
file_name
=
book
.
authors
[
0
]
.
name
+
'_'
+
file_name
file_name
=
helper
.
get_valid_filename
(
file_name
)
headers
=
{}
headers
=
Headers
()
try
:
headers
[
"Content-Type"
]
=
mimetypes
.
types_map
[
'.'
+
format
]
except
:
pass
headers
[
"Content-Disposition"
]
=
"attachment; filename*=UTF-8''
%
s.
%
s"
%
(
urllib
.
quote
(
file_name
.
encode
(
'utf-8'
)),
format
)
headers
[
"Content-Type"
]
=
mimetypes
.
types_map
[
'.'
+
book_
format
]
except
KeyError
:
headers
[
"Content-Type"
]
=
"application/octet-stream"
headers
[
"Content-Disposition"
]
=
"attachment; filename*=UTF-8''
%
s.
%
s"
%
(
quote
(
file_name
.
encode
(
'utf-8'
)),
book_
format
)
if
config
.
config_use_google_drive
:
df
=
gdriveutils
.
getFileFromEbooksFolder
(
Gdrive
.
Instance
()
.
drive
,
book
.
path
,
'
%
s.
%
s'
%
(
data
.
name
,
format
))
df
=
gdriveutils
.
getFileFromEbooksFolder
(
Gdrive
.
Instance
()
.
drive
,
book
.
path
,
'
%
s.
%
s'
%
(
data
.
name
,
book_
format
))
return
do_gdrive_download
(
df
,
headers
)
else
:
response
=
make_response
(
send_from_directory
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
),
data
.
name
+
"."
+
format
))
response
=
make_response
(
send_from_directory
(
os
.
path
.
join
(
config
.
config_calibre_dir
,
book
.
path
),
data
.
name
+
"."
+
book_
format
))
response
.
headers
=
headers
return
response
else
:
abort
(
404
)
@
app
.
route
(
"/download/<int:book_id>/<format>/<anyname>"
)
@
app
.
route
(
"/download/<int:book_id>/<
book_
format>/<anyname>"
)
@
login_required_if_no_ano
@
download_required
def
get_download_link_ext
(
book_id
,
format
,
anyname
):
return
get_download_link
(
book_id
,
format
)
def
get_download_link_ext
(
book_id
,
book_
format
,
anyname
):
return
get_download_link
(
book_id
,
book_
format
)
@
app
.
route
(
'/register'
,
methods
=
[
'GET'
,
'POST'
])
def
register
():
...
...
@@ -1686,7 +1675,7 @@ def register():
try
:
ub
.
session
.
add
(
content
)
ub
.
session
.
commit
()
except
Exception
as
e
:
except
Exception
:
ub
.
session
.
rollback
()
flash
(
_
(
u"An unknown error occured. Please try again later."
),
category
=
"error"
)
return
render_title_template
(
'register.html'
,
title
=
_
(
u"register"
))
...
...
@@ -1814,7 +1803,7 @@ def create_shelf():
ub
.
session
.
add
(
shelf
)
ub
.
session
.
commit
()
flash
(
_
(
u"Shelf
%(title)
s created"
,
title
=
to_save
[
"title"
]),
category
=
"success"
)
except
Exception
as
e
:
except
Exception
:
flash
(
_
(
u"There was an error"
),
category
=
"error"
)
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"create a shelf"
))
else
:
...
...
@@ -1842,7 +1831,7 @@ def edit_shelf(shelf_id):
try
:
ub
.
session
.
commit
()
flash
(
_
(
u"Shelf
%(title)
s changed"
,
title
=
to_save
[
"title"
]),
category
=
"success"
)
except
Exception
as
e
:
except
Exception
:
flash
(
_
(
u"There was an error"
),
category
=
"error"
)
return
render_title_template
(
'shelf_edit.html'
,
shelf
=
shelf
,
title
=
_
(
u"Edit a shelf"
))
else
:
...
...
@@ -1932,7 +1921,7 @@ def profile():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
translations
=
babel
.
list_translations
()
+
[
LC
(
'en'
)]
for
book
in
content
.
downloads
:
...
...
@@ -2146,7 +2135,7 @@ def new_user():
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
translations
=
[
LC
(
'en'
)]
+
babel
.
list_translations
()
if
request
.
method
==
"POST"
:
...
...
@@ -2251,7 +2240,7 @@ def edit_user(user_id):
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
translations
=
babel
.
list_translations
()
+
[
LC
(
'en'
)]
for
book
in
content
.
downloads
:
...
...
@@ -2373,17 +2362,17 @@ def edit_book(book_id):
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"title_sort"
,
1
,
db
.
title_sort
)
cc
=
db
.
session
.
query
(
db
.
Custom_Columns
)
.
filter
(
db
.
Custom_Columns
.
datatype
.
notin_
(
db
.
cc_exceptions
))
.
all
()
if
current_user
.
filter_language
()
!=
"all"
:
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
lang_
filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
filter
=
True
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
filter
)
.
first
()
lang_
filter
=
True
book
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
db
.
Books
.
id
==
book_id
)
.
filter
(
lang_
filter
)
.
first
()
author_names
=
[]
if
book
:
for
index
in
range
(
0
,
len
(
book
.
languages
)):
try
:
book
.
languages
[
index
]
.
language_name
=
LC
.
parse
(
book
.
languages
[
index
]
.
lang_code
)
.
get_language_name
(
get_locale
())
except
Exception
as
e
:
except
Exception
:
book
.
languages
[
index
]
.
language_name
=
_
(
isoLanguages
.
get
(
part3
=
book
.
languages
[
index
]
.
lang_code
)
.
name
)
for
author
in
book
.
authors
:
author_names
.
append
(
author
.
name
)
...
...
@@ -2441,7 +2430,7 @@ def edit_book(book_id):
for
lang
in
languages
:
try
:
lang
.
name
=
LC
.
parse
(
lang
.
lang_code
)
.
get_language_name
(
get_locale
())
.
lower
()
except
Exception
as
e
:
except
Exception
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
.
lower
()
for
inp_lang
in
input_languages
:
if
inp_lang
==
lang
.
name
:
...
...
@@ -2578,7 +2567,7 @@ def edit_book(book_id):
if
config
.
config_use_google_drive
:
updateGdriveCalibreFromLocal
()
if
"detail_view"
in
to_save
:
return
redirect
(
url_for
(
'show_book'
,
id
=
book
.
id
))
return
redirect
(
url_for
(
'show_book'
,
book_
id
=
book
.
id
))
else
:
return
render_title_template
(
'book_edit.html'
,
book
=
book
,
authors
=
author_names
,
cc
=
cc
,
title
=
_
(
u"edit metadata"
))
...
...
@@ -2600,9 +2589,9 @@ def upload():
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"title_sort"
,
1
,
db
.
title_sort
)
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
'uuid4'
,
0
,
lambda
:
str
(
uuid4
()))
if
request
.
method
==
'POST'
and
'btn-upload'
in
request
.
files
:
file
=
request
.
files
[
'btn-upload'
]
if
'.'
in
file
.
filename
:
file_ext
=
file
.
filename
.
rsplit
(
'.'
,
1
)[
-
1
]
.
lower
()
requested_
file
=
request
.
files
[
'btn-upload'
]
if
'.'
in
requested_
file
.
filename
:
file_ext
=
requested_
file
.
filename
.
rsplit
(
'.'
,
1
)[
-
1
]
.
lower
()
if
file_ext
not
in
ALLOWED_EXTENSIONS
:
flash
(
_
(
'File extension "
%
s" is not allowed to be uploaded to this server'
%
...
...
@@ -2613,7 +2602,7 @@ def upload():
else
:
flash
(
_
(
'File to be uploaded must have an extension'
),
category
=
"error"
)
return
redirect
(
url_for
(
'index'
))
meta
=
uploader
.
upload
(
file
)
meta
=
uploader
.
upload
(
requested_
file
)
title
=
meta
.
title
author
=
meta
.
author
...
...
@@ -2632,12 +2621,12 @@ def upload():
return
redirect
(
url_for
(
'index'
))
try
:
copyfile
(
meta
.
file_path
,
saved_filename
)
except
OSError
as
e
:
except
OSError
:
flash
(
_
(
u"Failed to store file
%
s (Permission denied)."
%
saved_filename
),
category
=
"error"
)
return
redirect
(
url_for
(
'index'
))
try
:
os
.
unlink
(
meta
.
file_path
)
except
OSError
as
e
:
except
OSError
:
flash
(
_
(
u"Failed to delete file
%
s (Permission denied)."
%
meta
.
file_path
),
category
=
"warning"
)
file_size
=
os
.
path
.
getsize
(
saved_filename
)
...
...
@@ -2700,6 +2689,8 @@ def upload():
book_in_shelfs
=
[]
return
render_title_template
(
'detail.html'
,
entry
=
db_book
,
cc
=
cc
,
title
=
db_book
.
title
,
books_shelfs
=
book_in_shelfs
,
)
else
:
return
redirect
(
url_for
(
"index"
))
def
start_gevent
():
from
gevent.wsgi
import
WSGIServer
...
...
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