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
9c1b3f13
Commit
9c1b3f13
authored
Mar 16, 2019
by
Ozzieisaacs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improved sorting for rated,random, hot books, read/unread book
parent
a66873a9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
445 additions
and
426 deletions
+445
-426
admin.py
cps/admin.py
+3
-2
editbooks.py
cps/editbooks.py
+2
-1
helper.py
cps/helper.py
+112
-0
opds.py
cps/opds.py
+2
-2
discover.html
cps/templates/discover.html
+0
-1
index.html
cps/templates/index.html
+7
-7
layout.html
cps/templates/layout.html
+1
-13
ub.py
cps/ub.py
+6
-6
web.py
cps/web.py
+312
-394
No files found.
cps/admin.py
View file @
9c1b3f13
...
...
@@ -25,8 +25,8 @@ import os
from
flask
import
Blueprint
,
flash
,
redirect
,
url_for
from
flask
import
abort
,
request
,
make_response
from
flask_login
import
login_required
,
current_user
,
logout_user
from
web
import
admin_required
,
render_title_template
,
before_request
,
speaking_language
,
unconfigured
,
\
login_required_if_no_ano
,
check_valid_domain
from
web
import
admin_required
,
render_title_template
,
before_request
,
unconfigured
,
\
login_required_if_no_ano
from
cps
import
db
,
ub
,
Server
,
get_locale
,
config
,
app
,
updater_thread
,
babel
import
json
from
datetime
import
datetime
,
timedelta
...
...
@@ -37,6 +37,7 @@ from babel import Locale as LC
from
sqlalchemy.exc
import
IntegrityError
from
gdriveutils
import
is_gdrive_ready
,
gdrive_support
,
downloadFile
,
deleteDatabaseOnChange
,
listRootFolders
import
helper
from
helper
import
speaking_language
,
check_valid_domain
from
werkzeug.security
import
generate_password_hash
try
:
from
imp
import
reload
...
...
cps/editbooks.py
View file @
9c1b3f13
...
...
@@ -31,8 +31,9 @@ import json
from
flask_babel
import
gettext
as
_
from
uuid
import
uuid4
import
helper
from
helper
import
order_authors
,
common_filters
from
flask_login
import
current_user
from
web
import
login_required_if_no_ano
,
common_filters
,
order_authors
,
render_title_template
,
edit_required
,
\
from
web
import
login_required_if_no_ano
,
render_title_template
,
edit_required
,
\
upload_required
,
login_required
,
EXTENSIONS_UPLOAD
import
gdriveutils
from
shutil
import
move
,
copyfile
...
...
cps/helper.py
View file @
9c1b3f13
...
...
@@ -32,9 +32,15 @@ from flask import send_from_directory, make_response, redirect, abort
from
flask_babel
import
gettext
as
_
from
flask_login
import
current_user
from
babel.dates
import
format_datetime
from
babel.core
import
UnknownLocaleError
from
datetime
import
datetime
from
babel
import
Locale
as
LC
import
shutil
import
requests
from
sqlalchemy.sql.expression
import
true
,
and_
,
false
,
text
,
func
from
iso639
import
languages
as
isoLanguages
from
pagination
import
Pagination
try
:
import
gdriveutils
as
gd
except
ImportError
:
...
...
@@ -558,3 +564,109 @@ def render_task_status(tasklist):
renderedtasklist
.
append
(
task
)
return
renderedtasklist
# Language and content filters for displaying in the UI
def
common_filters
():
if
current_user
.
filter_language
()
!=
"all"
:
lang_filter
=
db
.
Books
.
languages
.
any
(
db
.
Languages
.
lang_code
==
current_user
.
filter_language
())
else
:
lang_filter
=
true
()
content_rating_filter
=
false
()
if
current_user
.
mature_content
else
\
db
.
Books
.
tags
.
any
(
db
.
Tags
.
name
.
in_
(
config
.
mature_content_tags
()))
return
and_
(
lang_filter
,
~
content_rating_filter
)
# Creates for all stored languages a translated speaking name in the array for the UI
def
speaking_language
(
languages
=
None
):
if
not
languages
:
languages
=
db
.
session
.
query
(
db
.
Languages
)
.
all
()
for
lang
in
languages
:
try
:
cur_l
=
LC
.
parse
(
lang
.
lang_code
)
lang
.
name
=
cur_l
.
get_language_name
(
get_locale
())
except
UnknownLocaleError
:
lang
.
name
=
_
(
isoLanguages
.
get
(
part3
=
lang
.
lang_code
)
.
name
)
return
languages
# checks if domain is in database (including wildcards)
# example SELECT * FROM @TABLE WHERE 'abcdefg' LIKE Name;
# from https://code.luasoftware.com/tutorials/flask/execute-raw-sql-in-flask-sqlalchemy/
def
check_valid_domain
(
domain_text
):
domain_text
=
domain_text
.
split
(
'@'
,
1
)[
-
1
]
.
lower
()
sql
=
"SELECT * FROM registration WHERE :domain LIKE domain;"
result
=
ub
.
session
.
query
(
ub
.
Registration
)
.
from_statement
(
text
(
sql
))
.
params
(
domain
=
domain_text
)
.
all
()
return
len
(
result
)
# Orders all Authors in the list according to authors sort
def
order_authors
(
entry
):
sort_authors
=
entry
.
author_sort
.
split
(
'&'
)
authors_ordered
=
list
()
error
=
False
for
auth
in
sort_authors
:
# ToDo: How to handle not found authorname
result
=
db
.
session
.
query
(
db
.
Authors
)
.
filter
(
db
.
Authors
.
sort
==
auth
.
lstrip
()
.
strip
())
.
first
()
if
not
result
:
error
=
True
break
authors_ordered
.
append
(
result
)
if
not
error
:
entry
.
authors
=
authors_ordered
return
entry
# Fill indexpage with all requested data from database
def
fill_indexpage
(
page
,
database
,
db_filter
,
order
,
*
join
):
if
current_user
.
show_detail_random
():
randm
=
db
.
session
.
query
(
db
.
Books
)
.
filter
(
common_filters
())
\
.
order_by
(
func
.
random
())
.
limit
(
config
.
config_random_books
)
else
:
randm
=
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
(
common_filters
())
.
all
()))
entries
=
db
.
session
.
query
(
database
)
.
join
(
*
join
,
isouter
=
True
)
.
filter
(
db_filter
)
.
filter
(
common_filters
())
.
\
order_by
(
*
order
)
.
offset
(
off
)
.
limit
(
config
.
config_books_per_page
)
.
all
()
for
book
in
entries
:
book
=
order_authors
(
book
)
return
entries
,
randm
,
pagination
# read search results from calibre-database and return it (function is used for feed and simple search
def
get_search_results
(
term
):
q
=
list
()
authorterms
=
re
.
split
(
"[, ]+"
,
term
)
for
authorterm
in
authorterms
:
q
.
append
(
db
.
Books
.
authors
.
any
(
db
.
Authors
.
name
.
ilike
(
"
%
"
+
authorterm
+
"
%
"
)))
db
.
session
.
connection
()
.
connection
.
connection
.
create_function
(
"lower"
,
1
,
db
.
lcase
)
db
.
Books
.
authors
.
any
(
db
.
Authors
.
name
.
ilike
(
"
%
"
+
term
+
"
%
"
))
return
db
.
session
.
query
(
db
.
Books
)
.
filter
(
common_filters
())
.
filter
(
db
.
or_
(
db
.
Books
.
tags
.
any
(
db
.
Tags
.
name
.
ilike
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
series
.
any
(
db
.
Series
.
name
.
ilike
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
authors
.
any
(
and_
(
*
q
)),
db
.
Books
.
publishers
.
any
(
db
.
Publishers
.
name
.
ilike
(
"
%
"
+
term
+
"
%
"
)),
db
.
Books
.
title
.
ilike
(
"
%
"
+
term
+
"
%
"
)))
.
all
()
def
get_unique_other_books
(
library_books
,
author_books
):
# Get all identifiers (ISBN, Goodreads, etc) and filter author's books by that list so we show fewer duplicates
# Note: Not all images will be shown, even though they're available on Goodreads.com.
# See https://www.goodreads.com/topic/show/18213769-goodreads-book-images
identifiers
=
reduce
(
lambda
acc
,
book
:
acc
+
map
(
lambda
identifier
:
identifier
.
val
,
book
.
identifiers
),
library_books
,
[])
other_books
=
filter
(
lambda
book
:
book
.
isbn
not
in
identifiers
and
book
.
gid
[
"#text"
]
not
in
identifiers
,
author_books
)
# Fuzzy match book titles
if
feature_support
[
'levenshtein'
]:
library_titles
=
reduce
(
lambda
acc
,
book
:
acc
+
[
book
.
title
],
library_books
,
[])
other_books
=
filter
(
lambda
author_book
:
not
filter
(
lambda
library_book
:
# Remove items in parentheses before comparing
Levenshtein
.
ratio
(
re
.
sub
(
r"\(.*\)"
,
""
,
author_book
.
title
),
library_book
)
>
0.7
,
library_titles
),
other_books
)
return
other_books
cps/opds.py
View file @
9c1b3f13
...
...
@@ -30,12 +30,12 @@ import datetime
import
ub
from
flask_login
import
current_user
from
functools
import
wraps
from
web
import
login_required_if_no_ano
,
fill_indexpage
,
common_filters
,
get_search_results
,
render_read_books
from
web
import
login_required_if_no_ano
,
common_filters
,
get_search_results
,
render_read_books
,
download_required
from
sqlalchemy.sql.expression
import
func
,
text
import
helper
from
werkzeug.security
import
check_password_hash
from
werkzeug.datastructures
import
Headers
from
web
import
download_required
from
helper
import
fill_indexpage
import
sys
try
:
...
...
cps/templates/discover.html
View file @
9c1b3f13
...
...
@@ -3,7 +3,6 @@
<div
class=
"discover load-more"
>
<h2>
{{title}}
</h2>
<div
class=
"row"
>
{% for entry in entries %}
<div
class=
"col-sm-3 col-lg-2 col-xs-6 book"
>
<div
class=
"cover"
>
...
...
cps/templates/index.html
View file @
9c1b3f13
...
...
@@ -58,14 +58,14 @@ web. {% for entry in random %}
<div
class=
"discover load-more"
>
<h2
class=
"{{title}}"
>
{{_(title)}}
</h2>
<div
class=
"filterheader hidden-xs hidden-sm"
>
<a
id=
"new"
class=
"btn btn-primary"
href=
"{{url_for('web.
newest_books
')}}"
><span
class=
"glyphicon glyphicon-sort-by-order"
></span></a>
<a
id=
"old"
class=
"btn btn-primary"
href=
"{{url_for('web.
oldest_books
')}}"
><span
class=
"glyphicon glyphicon-sort-by-order-alt"
></span></a>
<a
id=
"asc"
class=
"btn btn-primary"
href=
"{{url_for('web.
titles_ascending
')}}"
><span
class=
"glyphicon glyphicon-font"
></span><span
class=
"glyphicon glyphicon-sort-by-alphabet"
></span></a>
<a
id=
"desc"
class=
"btn btn-primary"
href=
"{{url_for('web.
titles_descending
')}}"
><span
class=
"glyphicon glyphicon-font"
></span><span
class=
"glyphicon glyphicon-sort-by-alphabet-alt"
></span></a>
<a
id=
"pub_new"
class=
"btn btn-primary"
href=
"{{url_for('web.
titles_descending')}}"
><span
class=
"glyphicon glyphicon-calendar"
></span><span
class=
"glyphicon glyphicon-sort-by-order"
></a>
<a
id=
"pub_old"
class=
"btn btn-primary"
href=
"{{url_for('web.
titles_descending')}}"
><span
class=
"glyphicon glyphicon-calendar"
></span><span
class=
"glyphicon glyphicon-sort-by-order-alt"
></a>
<a
id=
"new"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='new
')}}"
><span
class=
"glyphicon glyphicon-sort-by-order"
></span></a>
<a
id=
"old"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='old
')}}"
><span
class=
"glyphicon glyphicon-sort-by-order-alt"
></span></a>
<a
id=
"asc"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='abc
')}}"
><span
class=
"glyphicon glyphicon-font"
></span><span
class=
"glyphicon glyphicon-sort-by-alphabet"
></span></a>
<a
id=
"desc"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='zyx
')}}"
><span
class=
"glyphicon glyphicon-font"
></span><span
class=
"glyphicon glyphicon-sort-by-alphabet-alt"
></span></a>
<a
id=
"pub_new"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='pubnew')}}"
><span
class=
"glyphicon glyphicon-calendar"
></span><span
class=
"glyphicon glyphicon-sort-by-order"
></span
></a>
<a
id=
"pub_old"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='pubold')}}"
><span
class=
"glyphicon glyphicon-calendar"
></span><span
class=
"glyphicon glyphicon-sort-by-order-alt"
></span
></a>
<div
class=
"btn-group character"
role=
"group"
>
<a
id=
"no_shelf"
class=
"btn btn-primary"
href=
"{{url_for('web.
titles_descending
')}}"
><span
class=
"glyphicon glyphicon-list"
></span><b>
?
</b></a>
<a
id=
"no_shelf"
class=
"btn btn-primary"
href=
"{{url_for('web.
books_list', data=page, sort='pubold
')}}"
><span
class=
"glyphicon glyphicon-list"
></span><b>
?
</b></a>
<div
id=
"all"
class=
"btn btn-primary"
>
{{_('All')}}
</div>
</div>
</div>
...
...
cps/templates/layout.html
View file @
9c1b3f13
...
...
@@ -121,22 +121,10 @@
<ul
class=
"list-unstyled"
id=
"scnd-nav"
intent
in-standard-append=
"nav.navigation"
in-mobile-after=
"#main-nav"
in-mobile-class=
"nav navbar-nav"
>
<li
class=
"nav-head hidden-xs"
>
{{_('Browse')}}
</li>
{% if g.user.check_visibility(1024) %}
<!-- ToDo: eliminate -->
<!--li id="nav_sort" class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<span class="glyphicon glyphicon-sort-by-attributes"></span>{{_('Sorted Books')}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li id="nav_sort_old" {% if page == 'newest' %}class="active"{% endif %}><a href="{{url_for('web.newest_books')}}">{{_('Sort By')}} {{_('Newest')}}</a></li>
<li id="nav_sort_new" {% if page == 'oldest' %}class="active"{% endif %}><a href="{{url_for('web.oldest_books')}}">{{_('Sort By')}} {{_('Oldest')}}</a></li>
<li id="nav_sort_asc" {% if page == 'a-z' %}class="active"{% endif %}><a href="{{url_for('web.titles_ascending')}}">{{_('Sort By')}} {{_('Title')}} ({{_('Ascending')}})</a></li>
<li id="nav_sort_desc" {% if page == 'z-a' %}class="active"{% endif %}><a href="{{url_for('web.titles_descending')}}">{{_('Sort By')}} {{_('Title')}} ({{_('Descending')}})</a></li>
</ul>
</li-->
{%endif%}
{% for element in sidebar %}
{% if g.user.check_visibility(element['visibility']) and element['public'] %}
<li
id=
"nav_{{element['id']}}"
{%
if
page =
=
element
['
page
']
%}
class=
"active"
{%
endif
%}
><a
href=
"{{url_for(element['link'])}}"
><span
class=
"glyphicon {{element['glyph']}}"
></span>
{{_(element['text'])}}
</a></li>
<li
id=
"nav_{{element['id']}}"
{%
if
page =
=
element
['
page
']
%}
class=
"active"
{%
endif
%}
><a
href=
"{{url_for(element['link']
, data=element['page'], sort='new'
)}}"
><span
class=
"glyphicon {{element['glyph']}}"
></span>
{{_(element['text'])}}
</a></li>
{% endif %}
{% endfor %}
...
...
cps/ub.py
View file @
9c1b3f13
...
...
@@ -107,21 +107,21 @@ def get_sidebar_config(kwargs=[]):
sidebar
.
append
({
"glyph"
:
"glyphicon-book"
,
"text"
:
_
(
'Recently Added'
),
"link"
:
'web.index'
,
"id"
:
"new"
,
"visibility"
:
SIDEBAR_RECENT
,
'public'
:
True
,
"page"
:
"root"
,
"show_text"
:
_
(
'Show recent books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-fire"
,
"text"
:
_
(
'Hot Books'
),
"link"
:
'web.
hot_books
'
,
"id"
:
"hot"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-fire"
,
"text"
:
_
(
'Hot Books'
),
"link"
:
'web.
books_list
'
,
"id"
:
"hot"
,
"visibility"
:
SIDEBAR_HOT
,
'public'
:
True
,
"page"
:
"hot"
,
"show_text"
:
_
(
'Show hot books'
),
"config_show"
:
True
})
sidebar
.
append
(
{
"glyph"
:
"glyphicon-star"
,
"text"
:
_
(
'Best rated Books'
),
"link"
:
'web.b
est_rated_books
'
,
"id"
:
"rated"
,
{
"glyph"
:
"glyphicon-star"
,
"text"
:
_
(
'Best rated Books'
),
"link"
:
'web.b
ooks_list
'
,
"id"
:
"rated"
,
"visibility"
:
SIDEBAR_BEST_RATED
,
'public'
:
True
,
"page"
:
"rated"
,
"show_text"
:
_
(
'Show best rated books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-eye-open"
,
"text"
:
_
(
'Read Books'
),
"link"
:
'web.
read_books
'
,
"id"
:
"read"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-eye-open"
,
"text"
:
_
(
'Read Books'
),
"link"
:
'web.
books_list
'
,
"id"
:
"read"
,
"visibility"
:
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"read"
,
"show_text"
:
_
(
'Show read and unread'
),
"config_show"
:
content
})
sidebar
.
append
(
{
"glyph"
:
"glyphicon-eye-close"
,
"text"
:
_
(
'Unread Books'
),
"link"
:
'web.
unread_books
'
,
"id"
:
"unread"
,
"visibility"
:
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"read"
,
{
"glyph"
:
"glyphicon-eye-close"
,
"text"
:
_
(
'Unread Books'
),
"link"
:
'web.
books_list
'
,
"id"
:
"unread"
,
"visibility"
:
SIDEBAR_READ_AND_UNREAD
,
'public'
:
(
not
g
.
user
.
is_anonymous
),
"page"
:
"
un
read"
,
"show_text"
:
_
(
'Show unread'
),
"config_show"
:
False
})
sidebar
.
append
({
"glyph"
:
"glyphicon-random"
,
"text"
:
_
(
'Discover'
),
"link"
:
'web.
discover
'
,
"id"
:
"rand"
,
sidebar
.
append
({
"glyph"
:
"glyphicon-random"
,
"text"
:
_
(
'Discover'
),
"link"
:
'web.
books_list
'
,
"id"
:
"rand"
,
"visibility"
:
SIDEBAR_RANDOM
,
'public'
:
True
,
"page"
:
"discover"
,
"show_text"
:
_
(
'Show random books'
),
"config_show"
:
True
})
sidebar
.
append
({
"glyph"
:
"glyphicon-inbox"
,
"text"
:
_
(
'Categories'
),
"link"
:
'web.category_list'
,
"id"
:
"cat"
,
...
...
cps/web.py
View file @
9c1b3f13
This diff is collapsed.
Click to expand it.
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