Refactored generating download links

pull/932/head
Ozzieisaacs 6 years ago
parent 9c1b3f136f
commit baf83b2f5a

@ -19,7 +19,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from cps import config, global_WorkerThread, get_locale, db from cps import config, global_WorkerThread, get_locale, db, mimetypes
from flask import current_app as app from flask import current_app as app
from tempfile import gettempdir from tempfile import gettempdir
import sys import sys
@ -40,6 +40,7 @@ import requests
from sqlalchemy.sql.expression import true, and_, false, text, func from sqlalchemy.sql.expression import true, and_, false, text, func
from iso639 import languages as isoLanguages from iso639 import languages as isoLanguages
from pagination import Pagination from pagination import Pagination
from werkzeug.datastructures import Headers
try: try:
import gdriveutils as gd import gdriveutils as gd
@ -49,12 +50,23 @@ import random
from subproc_wrapper import process_open from subproc_wrapper import process_open
import ub import ub
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
try: try:
import unidecode import unidecode
use_unidecode = True use_unidecode = True
except ImportError: except ImportError:
use_unidecode = False use_unidecode = False
try:
import Levenshtein
use_levenshtein = True
except ImportError:
use_levenshtein = False
def update_download(book_id, user_id): def update_download(book_id, user_id):
check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id == check = ub.session.query(ub.Downloads).filter(ub.Downloads.user_id == user_id).filter(ub.Downloads.book_id ==
@ -660,7 +672,7 @@ def get_unique_other_books(library_books, author_books):
author_books) author_books)
# Fuzzy match book titles # Fuzzy match book titles
if feature_support['levenshtein']: if use_levenshtein:
library_titles = reduce(lambda acc, book: acc + [book.title], library_books, []) library_titles = reduce(lambda acc, book: acc + [book.title], library_books, [])
other_books = filter(lambda author_book: not filter( other_books = filter(lambda author_book: not filter(
lambda library_book: lambda library_book:
@ -670,3 +682,40 @@ def get_unique_other_books(library_books, author_books):
), other_books) ), other_books)
return other_books return other_books
def get_cc_columns():
tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
if config.config_columns_to_ignore:
cc = []
for col in tmpcc:
r = re.compile(config.config_columns_to_ignore)
if r.match(col.label):
cc.append(col)
else:
cc = tmpcc
return cc
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 == book_format.upper()).first()
if data:
# collect downloaded books only for registered user and not for anonymous user
if current_user.is_authenticated:
ub.update_download(book_id, int(current_user.id))
file_name = book.title
if len(book.authors) > 0:
file_name = book.authors[0].name + '_' + file_name
file_name = get_valid_filename(file_name)
headers = Headers()
try:
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)
return do_download_file(book, book_format, data, headers)
else:
abort(404)

@ -22,7 +22,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# opds routing functions # opds routing functions
from cps import config, mimetypes, app, db from cps import config, db
from flask import request, render_template, Response, g, make_response from flask import request, render_template, Response, g, make_response
from pagination import Pagination from pagination import Pagination
from flask import Blueprint from flask import Blueprint
@ -34,7 +34,6 @@ from web import login_required_if_no_ano, common_filters, get_search_results, re
from sqlalchemy.sql.expression import func, text from sqlalchemy.sql.expression import func, text
import helper import helper
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
from werkzeug.datastructures import Headers
from helper import fill_indexpage from helper import fill_indexpage
import sys import sys
@ -58,7 +57,7 @@ def requires_basic_auth_if_no_ano(f):
return decorated return decorated
@opds.route("/opds") @opds.route("/opds/")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
def feed_index(): def feed_index():
return render_xml_template('index.xml') return render_xml_template('index.xml')
@ -258,25 +257,9 @@ def feed_shelf(book_id):
@opds.route("/opds/download/<book_id>/<book_format>/") @opds.route("/opds/download/<book_id>/<book_format>/")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano
@download_required @download_required
def get_opds_download_link(book_id, book_format): def opds_download_link(book_id, book_format):
book_format = book_format.split(".")[0] return helper.get_download_link(book_id,book_format)
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 == book_format.upper()).first()
app.logger.info(data.name)
if current_user.is_authenticated:
ub.update_download(book_id, int(current_user.id))
file_name = book.title
if len(book.authors) > 0:
file_name = book.authors[0].name + '_' + file_name
file_name = helper.get_valid_filename(file_name)
headers = Headers()
headers["Content-Disposition"] = "attachment; filename*=UTF-8''%s.%s" % (quote(file_name.encode('utf8')),
book_format)
try:
headers["Content-Type"] = mimetypes.types_map['.' + book_format]
except KeyError:
headers["Content-Type"] = "application/octet-stream"
return helper.do_download_file(book, book_format, data, headers)
@opds.route("/ajax/book/<string:uuid>") @opds.route("/ajax/book/<string:uuid>")
@requires_basic_auth_if_no_ano @requires_basic_auth_if_no_ano

@ -22,7 +22,7 @@
{{_('Download')}} : {{_('Download')}} :
</button> </button>
{% for format in entry.data %} {% for format in entry.data %}
<a href="{{ url_for('web.get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}" id="btnGroupDrop1{{format.format|lower}}" class="btn btn-primary" role="button"> <a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}" id="btnGroupDrop1{{format.format|lower}}" class="btn btn-primary" role="button">
<span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }}) <span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }})
</a> </a>
{% endfor %} {% endfor %}
@ -33,7 +33,7 @@
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1"> <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
{% for format in entry.data %} {% for format in entry.data %}
<li><a href="{{ url_for('web.get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}">{{format.format}} ({{ format.uncompressed_size|filesizeformat }})</a></li> <li><a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}">{{format.format}} ({{ format.uncompressed_size|filesizeformat }})</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}

@ -65,7 +65,7 @@
<link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.id)}}" rel="http://opds-spec.org/image/thumbnail"/> <link type="image/jpeg" href="{{url_for('opds.feed_get_cover', book_id=entry.id)}}" rel="http://opds-spec.org/image/thumbnail"/>
{% endif %} {% endif %}
{% for format in entry.data %} {% for format in entry.data %}
<link rel="http://opds-spec.org/acquisition" href="{{ url_for('opds.get_opds_download_link', book_id=entry.id, book_format=format.format|lower)}}" <link rel="http://opds-spec.org/acquisition" href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower)}}"
length="{{format.uncompressed_size}}" mtime="{{entry.atom_timestamp}}" type="{{format.format|lower|mimetype}}"/> length="{{format.uncompressed_size}}" mtime="{{entry.atom_timestamp}}" type="{{format.format|lower|mimetype}}"/>
{% endfor %} {% endfor %}
</entry> </entry>

@ -36,7 +36,7 @@
"timestamp": "{{entry.timestamp}}", "timestamp": "{{entry.timestamp}}",
"thumbnail": "{{url_for('opds.feed_get_cover', book_id=entry.id)}}", "thumbnail": "{{url_for('opds.feed_get_cover', book_id=entry.id)}}",
"main_format": { "main_format": {
"{{entry.data[0].format|lower}}": "{{ url_for('opds.get_opds_download_link', book_id=entry.id, book_format=entry.data[0].format|lower)}}" "{{entry.data[0].format|lower}}": "{{ url_for('web.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 %}, "rating":{% if entry.ratings.__len__() > 0 %} "{{entry.ratings[0].rating}}.0"{% else %}0.0{% endif %},
"authors": [ "authors": [
@ -47,7 +47,7 @@
"other_formats": { "other_formats": {
{% if entry.data.__len__() > 1 %} {% if entry.data.__len__() > 1 %}
{% for format in entry.data[1:] %} {% for format in entry.data[1:] %}
"{{format.format|lower}}": "{{ url_for('opds.get_opds_download_link', book_id=entry.id, book_format=format.format|lower)}}"{% if not loop.last %},{% endif %} "{{format.format|lower}}": "{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower)}}"{% if not loop.last %},{% endif %}
{% endfor %} {% endfor %}
{% endif %} }, {% endif %} },
"title_sort": "{{entry.sort}}" "title_sort": "{{entry.sort}}"

@ -84,7 +84,7 @@
filePath: "{{ url_for('static', filename='js/libs/') }}", filePath: "{{ url_for('static', filename='js/libs/') }}",
cssPath: "{{ url_for('static', filename='css/') }}", cssPath: "{{ url_for('static', filename='css/') }}",
bookmarkUrl: "{{ url_for('web.bookmark', book_id=bookid, book_format='EPUB') }}", bookmarkUrl: "{{ url_for('web.bookmark', book_id=bookid, book_format='EPUB') }}",
bookUrl: "{{ url_for('web.get_download_link_ext', book_id=bookid, book_format='epub', anyname='file.epub') }}", bookUrl: "{{ url_for('web.download_link', book_id=bookid, book_format='epub', anyname='file.epub') }}",
bookmark: "{{ bookmark.bookmark_key if bookmark != None }}", bookmark: "{{ bookmark.bookmark_key if bookmark != None }}",
useBookmarks: "{{ g.user.is_authenticated | tojson }}" useBookmarks: "{{ g.user.is_authenticated | tojson }}"
}; };

@ -53,7 +53,7 @@
{% if entry.data|length < 2 %} {% if entry.data|length < 2 %}
{% for format in entry.data %} {% for format in entry.data %}
<a href="{{ url_for('web.get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}" id="btnGroupDrop1{{format.format|lower}}" class="btn btn-primary" role="button"> <a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}" id="btnGroupDrop1{{format.format|lower}}" class="btn btn-primary" role="button">
<span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }}) <span class="glyphicon glyphicon-download"></span>{{format.format}} ({{ format.uncompressed_size|filesizeformat }})
</a> </a>
{% endfor %} {% endfor %}
@ -64,7 +64,7 @@
</button> </button>
<ul class="dropdown-menu" aria-labelledby="btnGroupDrop1"> <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
{% for format in entry.data %} {% for format in entry.data %}
<li><a href="{{ url_for('get_download_link_ext', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}">{{format.format}} ({{ format.uncompressed_size|filesizeformat }})</a></li> <li><a href="{{ url_for('web.download_link', book_id=entry.id, book_format=format.format|lower, anyname=entry.id|string+'.'+format.format|lower) }}">{{format.format}} ({{ format.uncompressed_size|filesizeformat }})</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}

@ -37,7 +37,7 @@ from babel import Locale as LC
from babel.dates import format_date from babel.dates import format_date
from babel.core import UnknownLocaleError from babel.core import UnknownLocaleError
import base64 import base64
from sqlalchemy.sql.expression import text, func, true, and_, false, not_ from sqlalchemy.sql.expression import text, func, true, false, not_
import json import json
import datetime import datetime
from iso639 import languages as isoLanguages from iso639 import languages as isoLanguages
@ -63,7 +63,7 @@ except ImportError:
feature_support['ldap'] = False feature_support['ldap'] = False
try: try:
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpErrort
except ImportError: except ImportError:
pass pass
@ -73,12 +73,6 @@ try:
except ImportError: except ImportError:
feature_support['goodreads'] = False feature_support['goodreads'] = False
try:
import Levenshtein
feature_support['levenshtein'] = True
except ImportError:
feature_support['levenshtein'] = False
try: try:
from functools import reduce, wraps from functools import reduce, wraps
except ImportError: except ImportError:
@ -110,7 +104,7 @@ EXTENSIONS_UPLOAD = {'txt', 'pdf', 'epub', 'mobi', 'azw', 'azw3', 'cbr', 'cbz',
'fb2', 'html', 'rtf', 'odt', 'mp3', 'm4a', 'm4b'} 'fb2', 'html', 'rtf', 'odt', 'mp3', 'm4a', 'm4b'}
'''EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] + '''EXTENSIONS_READER = set(['txt', 'pdf', 'epub', 'zip', 'cbz', 'tar', 'cbt'] +
(['rar','cbr'] if feature_support['rar'] else []))''' (['rar','cbr'] if feature_support['rar'] else []))'''
@ -848,7 +842,8 @@ def search():
@login_required_if_no_ano @login_required_if_no_ano
def advanced_search(): def advanced_search():
# Build custom columns names # Build custom columns names
tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all() cc = helper.get_cc_columns()
'''tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
if config.config_columns_to_ignore: if config.config_columns_to_ignore:
cc = [] cc = []
for col in tmpcc: for col in tmpcc:
@ -856,7 +851,7 @@ def advanced_search():
if r.match(col.label): if r.match(col.label):
cc.append(col) cc.append(col)
else: else:
cc = tmpcc cc = tmpcc'''
db.session.connection().connection.connection.create_function("lower", 1, db.lcase) db.session.connection().connection.connection.create_function("lower", 1, db.lcase)
q = db.session.query(db.Books) q = db.session.query(db.Books)
@ -1074,39 +1069,12 @@ def serve_book(book_id, book_format):
return send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format) return send_from_directory(os.path.join(config.config_calibre_dir, book.path), data.name + "." + book_format)
@web.route("/download/<int:book_id>/<book_format>") @web.route("/download/<int:book_id>/<book_format>", defaults={'anyname': 'None'})
@login_required_if_no_ano
@download_required
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 == book_format.upper()).first()
if data:
# collect downloaded books only for registered user and not for anonymous user
if current_user.is_authenticated:
ub.update_download(book_id, int(current_user.id))
file_name = book.title
if len(book.authors) > 0:
file_name = book.authors[0].name + '_' + file_name
file_name = helper.get_valid_filename(file_name)
headers = Headers()
try:
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)
return helper.do_download_file(book, book_format, data, headers)
else:
abort(404)
@web.route("/download/<int:book_id>/<book_format>/<anyname>") @web.route("/download/<int:book_id>/<book_format>/<anyname>")
@login_required_if_no_ano @login_required_if_no_ano
@download_required @download_required
def get_download_link_ext(book_id, book_format, anyname): def download_link(book_id, book_format, anyname):
return get_download_link(book_id, book_format) return helper.get_download_link(book_id, book_format)
@web.route('/send/<int:book_id>/<book_format>/<int:convert>') @web.route('/send/<int:book_id>/<book_format>/<int:convert>')
@ -1457,7 +1425,8 @@ def show_book(book_id):
except UnknownLocaleError: except UnknownLocaleError:
entries.languages[index].language_name = _( entries.languages[index].language_name = _(
isoLanguages.get(part3=entries.languages[index].lang_code).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() cc = helper.get_cc_columns()
'''tmpcc = db.session.query(db.Custom_Columns).filter(db.Custom_Columns.datatype.notin_(db.cc_exceptions)).all()
if config.config_columns_to_ignore: if config.config_columns_to_ignore:
cc = [] cc = []
@ -1466,7 +1435,7 @@ def show_book(book_id):
if r.match(col.label): if r.match(col.label):
cc.append(col) cc.append(col)
else: else:
cc = tmpcc cc = tmpcc'''
book_in_shelfs = [] book_in_shelfs = []
shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).all() shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).all()
for entry in shelfs: for entry in shelfs:

Loading…
Cancel
Save