mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 07:13:02 +00:00 
			
		
		
		
	Refactored generating download links
This commit is contained in:
		| @@ -19,7 +19,7 @@ | ||||
| #  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 tempfile import gettempdir | ||||
| import sys | ||||
| @@ -40,6 +40,7 @@ import requests | ||||
| from sqlalchemy.sql.expression import true, and_, false, text, func | ||||
| from iso639 import languages as isoLanguages | ||||
| from pagination import Pagination | ||||
| from werkzeug.datastructures import Headers | ||||
|  | ||||
| try: | ||||
|     import gdriveutils as gd | ||||
| @@ -49,12 +50,23 @@ import random | ||||
| from subproc_wrapper import process_open | ||||
| import ub | ||||
|  | ||||
| try: | ||||
|     from urllib.parse import quote | ||||
| except ImportError: | ||||
|     from urllib import quote | ||||
|  | ||||
| try: | ||||
|     import unidecode | ||||
|     use_unidecode = True | ||||
| except ImportError: | ||||
|     use_unidecode = False | ||||
|  | ||||
| try: | ||||
|     import Levenshtein | ||||
|     use_levenshtein = True | ||||
| except ImportError: | ||||
|     use_levenshtein = False | ||||
|  | ||||
|  | ||||
| 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 == | ||||
| @@ -660,7 +672,7 @@ def get_unique_other_books(library_books, author_books): | ||||
|                          author_books) | ||||
|  | ||||
|     # Fuzzy match book titles | ||||
|     if feature_support['levenshtein']: | ||||
|     if use_levenshtein: | ||||
|         library_titles = reduce(lambda acc, book: acc + [book.title], library_books, []) | ||||
|         other_books = filter(lambda author_book: not filter( | ||||
|             lambda library_book: | ||||
| @@ -670,3 +682,40 @@ def get_unique_other_books(library_books, author_books): | ||||
|         ), 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) | ||||
|   | ||||
							
								
								
									
										27
									
								
								cps/opds.py
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								cps/opds.py
									
									
									
									
									
								
							| @@ -22,7 +22,7 @@ | ||||
| #  along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
|  | ||||
| # 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 pagination import Pagination | ||||
| 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 | ||||
| import helper | ||||
| from werkzeug.security import check_password_hash | ||||
| from werkzeug.datastructures import Headers | ||||
| from helper import fill_indexpage | ||||
| import sys | ||||
|  | ||||
| @@ -58,7 +57,7 @@ def requires_basic_auth_if_no_ano(f): | ||||
|     return decorated | ||||
|  | ||||
|  | ||||
| @opds.route("/opds") | ||||
| @opds.route("/opds/") | ||||
| @requires_basic_auth_if_no_ano | ||||
| def feed_index(): | ||||
|     return render_xml_template('index.xml') | ||||
| @@ -258,25 +257,9 @@ def feed_shelf(book_id): | ||||
| @opds.route("/opds/download/<book_id>/<book_format>/") | ||||
| @requires_basic_auth_if_no_ano | ||||
| @download_required | ||||
| def get_opds_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() | ||||
|     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) | ||||
| def opds_download_link(book_id, book_format): | ||||
|     return helper.get_download_link(book_id,book_format) | ||||
|  | ||||
|  | ||||
| @opds.route("/ajax/book/<string:uuid>") | ||||
| @requires_basic_auth_if_no_ano | ||||
|   | ||||
| @@ -22,7 +22,7 @@ | ||||
|                     {{_('Download')}} : | ||||
|                   </button> | ||||
|                   {% 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 }}) | ||||
|                   </a> | ||||
|                   {% endfor %} | ||||
| @@ -33,7 +33,7 @@ | ||||
|                   </button> | ||||
|                   <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1"> | ||||
|                   {% 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 %} | ||||
|                   </ul> | ||||
|                 {% 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"/> | ||||
|     {% endif %} | ||||
|     {% 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}}"/> | ||||
|     {% endfor %} | ||||
|   </entry> | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|   "timestamp": "{{entry.timestamp}}", | ||||
|   "thumbnail": "{{url_for('opds.feed_get_cover', book_id=entry.id)}}", | ||||
|   "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 %}, | ||||
|   "authors": [ | ||||
| @@ -47,7 +47,7 @@ | ||||
|   "other_formats": { | ||||
|   {% if entry.data.__len__() > 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 %} | ||||
|   {% endif %} }, | ||||
|   "title_sort": "{{entry.sort}}" | ||||
|   | ||||
| @@ -84,7 +84,7 @@ | ||||
|               filePath: "{{ url_for('static', filename='js/libs/') }}", | ||||
|               cssPath: "{{ url_for('static', filename='css/') }}", | ||||
|               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 }}", | ||||
|               useBookmarks: "{{ g.user.is_authenticated | tojson }}" | ||||
|           }; | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|                 {% if entry.data|length < 2 %} | ||||
|  | ||||
|                   {% 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 }}) | ||||
|                   </a> | ||||
|                   {% endfor %} | ||||
| @@ -64,7 +64,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, 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 %} | ||||
|                   </ul> | ||||
|                 {% endif %} | ||||
|   | ||||
							
								
								
									
										53
									
								
								cps/web.py
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								cps/web.py
									
									
									
									
									
								
							| @@ -37,7 +37,7 @@ from babel import Locale as LC | ||||
| from babel.dates import format_date | ||||
| from babel.core import UnknownLocaleError | ||||
| 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 datetime | ||||
| from iso639 import languages as isoLanguages | ||||
| @@ -63,7 +63,7 @@ except ImportError: | ||||
|     feature_support['ldap'] = False | ||||
|  | ||||
| try: | ||||
|     from googleapiclient.errors import HttpError | ||||
|     from googleapiclient.errors import HttpErrort | ||||
| except ImportError: | ||||
|     pass | ||||
|  | ||||
| @@ -73,12 +73,6 @@ try: | ||||
| except ImportError: | ||||
|     feature_support['goodreads'] = False | ||||
|  | ||||
| try: | ||||
|     import Levenshtein | ||||
|     feature_support['levenshtein'] = True | ||||
| except ImportError: | ||||
|     feature_support['levenshtein'] = False | ||||
|  | ||||
| try: | ||||
|     from functools import reduce, wraps | ||||
| except ImportError: | ||||
| @@ -848,7 +842,8 @@ def search(): | ||||
| @login_required_if_no_ano | ||||
| def advanced_search(): | ||||
|     # 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: | ||||
|         cc = [] | ||||
|         for col in tmpcc: | ||||
| @@ -856,7 +851,7 @@ def advanced_search(): | ||||
|             if r.match(col.label): | ||||
|                 cc.append(col) | ||||
|     else: | ||||
|         cc = tmpcc | ||||
|         cc = tmpcc''' | ||||
|  | ||||
|     db.session.connection().connection.connection.create_function("lower", 1, db.lcase) | ||||
|     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) | ||||
|  | ||||
|  | ||||
| @web.route("/download/<int:book_id>/<book_format>") | ||||
| @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>", defaults={'anyname': 'None'}) | ||||
| @web.route("/download/<int:book_id>/<book_format>/<anyname>") | ||||
| @login_required_if_no_ano | ||||
| @download_required | ||||
| def get_download_link_ext(book_id, book_format, anyname): | ||||
|     return get_download_link(book_id, book_format) | ||||
| def download_link(book_id, book_format, anyname): | ||||
|     return helper.get_download_link(book_id, book_format) | ||||
|  | ||||
|  | ||||
| @web.route('/send/<int:book_id>/<book_format>/<int:convert>') | ||||
| @@ -1457,7 +1425,8 @@ def show_book(book_id): | ||||
|             except UnknownLocaleError: | ||||
|                 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() | ||||
|         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: | ||||
|             cc = [] | ||||
| @@ -1466,7 +1435,7 @@ def show_book(book_id): | ||||
|                 if r.match(col.label): | ||||
|                     cc.append(col) | ||||
|         else: | ||||
|             cc = tmpcc | ||||
|             cc = tmpcc''' | ||||
|         book_in_shelfs = [] | ||||
|         shelfs = ub.session.query(ub.BookShelf).filter(ub.BookShelf.book_id == book_id).all() | ||||
|         for entry in shelfs: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Ozzieisaacs
					Ozzieisaacs