From 6717683ac3cfcf15ecbce1c1ec667e01038bb21f Mon Sep 17 00:00:00 2001 From: Ozzie Isaacs Date: Sun, 18 Aug 2024 16:27:02 +0200 Subject: [PATCH] Improvements merge metadata upload format Music icon only visible once if more than one audio format available --- cps/editbooks.py | 81 +++++++++++++++++----------------- cps/isoLanguages.py | 17 +++---- cps/jinjia.py | 11 ++++- cps/search.py | 6 +-- cps/static/js/edit_books.js | 8 ++-- cps/static/js/get_meta.js | 6 +-- cps/templates/author.html | 6 +-- cps/templates/book_edit.html | 12 ++--- cps/templates/image.html | 2 +- cps/templates/index.html | 6 +-- cps/templates/search.html | 6 +-- cps/templates/search_form.html | 10 ++--- cps/web.py | 4 +- 13 files changed, 90 insertions(+), 85 deletions(-) diff --git a/cps/editbooks.py b/cps/editbooks.py index 95d0a766..80e73697 100644 --- a/cps/editbooks.py +++ b/cps/editbooks.py @@ -455,13 +455,13 @@ def do_edit_book(book_id, upload_formats=None): # Update folder of book on local disk edited_books_id = None title_author_error = None - upload_format = False + upload_mode = False # handle book title change - if "book_title" in to_save: - title_change = handle_title_on_edit(book, to_save["book_title"]) + if "title" in to_save: + title_change = handle_title_on_edit(book, to_save["title"]) # handle book author change if not upload_formats: - input_authors, author_change = handle_author_on_edit(book, to_save["author_name"]) + input_authors, author_change = handle_author_on_edit(book, to_save["authors"]) if author_change or title_change: edited_books_id = book.id modify_date = True @@ -475,10 +475,9 @@ def do_edit_book(book_id, upload_formats=None): # handle book ratings modify_date |= edit_book_ratings(to_save, book) - meta = True else: # handle upload other formats from local disk - to_save, upload_format = upload_book_formats(upload_formats, book, book_id, book.has_cover) + to_save, edit_error = upload_book_formats(upload_formats, book, book_id, book.has_cover) # handle upload covers from local disk cover_upload_success = upload_cover(request, book) if cover_upload_success or to_save.get("format_cover"): @@ -507,7 +506,7 @@ def do_edit_book(book_id, upload_formats=None): # Add default series_index to book modify_date |= edit_book_series_index(to_save.get("series_index"), book) # Handle book comments/description - modify_date |= edit_book_comments(Markup(to_save.get('description')).unescape(), book) + modify_date |= edit_book_comments(Markup(to_save.get('comments')).unescape(), book) # Handle identifiers input_identifiers = identifier_list(to_save, book) modification, warning = modify_identifiers(input_identifiers, book.identifiers, calibre_db.session) @@ -522,7 +521,12 @@ def do_edit_book(book_id, upload_formats=None): modify_date |= edit_book_publisher(to_save.get('publisher'), book) # handle book languages try: - modify_date |= edit_book_languages(to_save.get('languages'), book, upload_format) + invalid = [] + modify_date |= edit_book_languages(to_save.get('languages'), book, upload_mode=upload_formats, + invalid=invalid) + if invalid: + for lang in invalid: + flash(_("'%(langname)s' is not a valid language", langname=lang), category="warning") except ValueError as e: flash(str(e), category="error") edit_error = True @@ -549,9 +553,7 @@ def do_edit_book(book_id, upload_formats=None): calibre_db.session.commit() if config.config_use_google_drive: gdriveutils.updateGdriveCalibreFromLocal() - # if format is upladed and something goes wrong to_save is set to empty dictonary - if (len(to_save) - and edit_error is not True and title_author_error is not True and cover_upload_success is not False): + if edit_error is not True and title_author_error is not True and cover_upload_success is not False: flash(_("Metadata successfully updated"), category="success") if upload_formats: @@ -579,19 +581,20 @@ def do_edit_book(book_id, upload_formats=None): return redirect(url_for('web.show_book', book_id=book.id)) -def merge_metadata(to_save, meta): - if not to_save.get("languages") and meta.languages: - upload_language = True - else: - upload_language = False +def merge_metadata(book, meta, to_save): + if meta.cover: + to_save['cover_format'] = meta.cover for s_field, m_field in [ - ('tags', 'tags'), ('author_name', 'author'), ('series', 'series'), + ('tags', 'tags'), ('authors', 'author'), ('series', 'series'), ('series_index', 'series_id'), ('languages', 'languages'), - ('book_title', 'title'), ('description', 'description'),('format_cover', 'cover')]: - val = getattr(meta, m_field, '') + ('title', 'title'), ('comments', 'description')]: + try: + val = None if len(getattr(book, s_field)) else getattr(meta, m_field, '') + except TypeError: + val = None if len(str(getattr(book, s_field))) else getattr(meta, m_field, '') if val: to_save[s_field] = val - return upload_language + def identifier_list(to_save, book): """Generate a list of Identifiers from form information""" @@ -1054,10 +1057,7 @@ def edit_book_languages(languages, book, upload_mode=False, invalid=None): if languages: input_languages = languages.split(',') unknown_languages = [] - if not upload_mode: - input_l = isoLanguages.get_language_codes(get_locale(), input_languages, unknown_languages) - else: - input_l = isoLanguages.get_valid_language_codes(get_locale(), input_languages, unknown_languages) + input_l = isoLanguages.get_language_code_from_name(get_locale(), input_languages, unknown_languages) for lang in unknown_languages: log.error("'%s' is not a valid language", lang) if isinstance(invalid, list): @@ -1071,7 +1071,7 @@ def edit_book_languages(languages, book, upload_mode=False, invalid=None): if input_l[0] != current_user.filter_language() and current_user.filter_language() != "all": input_l[0] = calibre_db.session.query(db.Languages). \ filter(db.Languages.lang_code == current_user.filter_language()).first().lang_code - # Remove duplicates + # Remove duplicates from normalized langcodes input_l = helper.uniq(input_l) return modify_database_object(input_l, book.languages, db.Languages, calibre_db.session, 'languages') return False @@ -1200,37 +1200,35 @@ def edit_cc_data(book_id, book, to_save, cc): return changed -# returns None if no file is uploaded -# returns False if an error occurs, in all other cases the ebook metadata is returned +# returns False if an error occurs or no book is uploaded, in all other cases the ebook metadata to change is returned def upload_book_formats(requested_files, book, book_id, no_cover=True): # Check and handle Uploaded file to_save = dict() - upload_format = False + error = False allowed_extensions = config.config_upload_formats.split(',') for requested_file in requested_files: current_filename = requested_file.filename if config.config_check_extensions and allowed_extensions != ['']: if not validate_mime_type(requested_file, allowed_extensions): flash(_("File type isn't allowed to be uploaded to this server"), category="error") + error = True continue - # return False - # check for empty request if current_filename != '': if not current_user.role_upload(): flash(_("User has no rights to upload additional file formats"), category="error") + error = True continue - # return False if '.' in current_filename: file_ext = current_filename.rsplit('.', 1)[-1].lower() if file_ext not in allowed_extensions and '' not in allowed_extensions: flash(_("File extension '%(ext)s' is not allowed to be uploaded to this server", ext=file_ext), category="error") + error = True continue - # return False else: flash(_('File to be uploaded must have an extension'), category="error") + error = True continue - # return False file_name = book.path.rsplit('/', 1)[-1] filepath = os.path.normpath(os.path.join(config.get_book_path(), book.path)) @@ -1243,14 +1241,14 @@ def upload_book_formats(requested_files, book, book_id, no_cover=True): except OSError: flash(_("Failed to create path %(path)s (Permission denied).", path=filepath), category="error") + error = True continue - # return False try: requested_file.save(saved_filename) except OSError: flash(_("Failed to store file %(file)s.", file=saved_filename), category="error") + error = True continue - # return False file_size = os.path.getsize(saved_filename) @@ -1268,8 +1266,8 @@ def upload_book_formats(requested_files, book, book_id, no_cover=True): log.error_or_exception("Database error: {}".format(e)) flash(_("Oops! Database Error: %(error)s.", error=e.orig if hasattr(e, "orig") else e), category="error") + error = True continue - # return False # return redirect(url_for('web.show_book', book_id=book.id)) # Queue uploader info link = '{}'.format(url_for('web.show_book', book_id=book.id), escape(book.title)) @@ -1280,8 +1278,13 @@ def upload_book_formats(requested_files, book, book_id, no_cover=True): *os.path.splitext(current_filename), rar_executable=config.config_rarfile_location, no_cover=no_cover) - upload_format |= merge_metadata(to_save, meta) - return to_save if len(to_save) else False, upload_format + merge_metadata(book, meta, to_save) + if to_save.get('languages'): + langs = [] + for lang_code in to_save['languages']: + langs.append(isoLanguages.get_language_name(get_locale(), lang_code)) + to_save['languages'] = ",".join(langs) + return to_save, error def upload_cover(cover_request, book): @@ -1315,7 +1318,6 @@ def handle_title_on_edit(book, book_title): def handle_author_on_edit(book, author_name, update_stored=True): change = False - # handle author(s) input_authors = prepare_authors(author_name, config.get_book_path(), config.config_use_google_drive) # Search for each author if author is in database, if not, author name and sorted author name is generated new @@ -1345,7 +1347,6 @@ def search_objects_remove(db_book_object, db_type, input_elements): if db_type == 'custom': type_elements = c_elements.value else: - # type_elements = c_elements.name type_elements = c_elements for inp_element in input_elements: if type_elements == inp_element: diff --git a/cps/isoLanguages.py b/cps/isoLanguages.py index 57473658..8a8413ff 100644 --- a/cps/isoLanguages.py +++ b/cps/isoLanguages.py @@ -18,6 +18,7 @@ from .iso_language_names import LANGUAGE_NAMES as _LANGUAGE_NAMES from . import logger +from .string_helper import strip_whitespaces log = logger.create() @@ -69,20 +70,20 @@ def get_language_name(locale, lang_code): return name -def get_language_codes(locale, language_names, remainder=None): - language_names = set(x.strip().lower() for x in language_names if x) +def get_language_code_from_name(locale, language_names, remainder=None): + language_names = set(strip_whitespaces(x).lower() for x in language_names if x) lang = list() - for k, v in get_language_names(locale).items(): - v = v.lower() - if v in language_names: - lang.append(k) - language_names.remove(v) + for key, val in get_language_names(locale).items(): + val = val.lower() + if val in language_names: + lang.append(key) + language_names.remove(val) if remainder is not None and language_names: remainder.extend(language_names) return lang -def get_valid_language_codes(locale, language_names, remainder=None): +def get_valid_language_codes_from_code(locale, language_names, remainder=None): lang = list() if "" in language_names: language_names.remove("") diff --git a/cps/jinjia.py b/cps/jinjia.py index 01abf5a6..39c7f20c 100644 --- a/cps/jinjia.py +++ b/cps/jinjia.py @@ -27,7 +27,7 @@ import datetime import mimetypes from uuid import uuid4 -from flask import Blueprint, request, url_for +from flask import Blueprint, request, url_for, g from flask_babel import format_date from .cw_login import current_user @@ -182,3 +182,12 @@ def get_cover_srcset(series): url = url_for('web.get_series_cover', series_id=series.id, resolution=shortname, c=cache_timestamp()) srcset.append(f'{url} {resolution}x') return ', '.join(srcset) + + +@jinjia.app_template_filter('music') +def contains_music(book_formats): + result = False + for format in book_formats: + if format.format.lower() in g.constants.EXTENSIONS_AUDIO: + result = True + return result diff --git a/cps/search.py b/cps/search.py index 94b064a7..29dda16d 100644 --- a/cps/search.py +++ b/cps/search.py @@ -258,14 +258,14 @@ def render_adv_search_results(term, offset=None, order=None, limit=None): tags['include_' + element] = term.get('include_' + element) tags['exclude_' + element] = term.get('exclude_' + element) - author_name = term.get("author_name") - book_title = term.get("book_title") + author_name = term.get("authors") + book_title = term.get("title") publisher = term.get("publisher") pub_start = term.get("publishstart") pub_end = term.get("publishend") rating_low = term.get("ratinghigh") rating_high = term.get("ratinglow") - description = term.get("comment") + description = term.get("comments") read_status = term.get("read_status") if author_name: author_name = strip_whitespaces(author_name).lower().replace(',', '|') diff --git a/cps/static/js/edit_books.js b/cps/static/js/edit_books.js index e794d9a4..7cc6c4db 100644 --- a/cps/static/js/edit_books.js +++ b/cps/static/js/edit_books.js @@ -3,9 +3,9 @@ */ /* global Bloodhound, language, Modernizr, tinymce, getPath */ -if ($("#description").length) { +if ($("#comments").length) { tinymce.init({ - selector: "#description", + selector: "#comments", plugins: 'code', branding: false, menubar: "edit view format", @@ -261,8 +261,8 @@ $("#btn-upload-cover").on("change", function () { $("#xchange").click(function () { this.blur(); - var title = $("#book_title").val(); - $("#book_title").val($("#bookAuthor").val()); + var title = $("#title").val(); + $("#title").val($("#bookAuthor").val()); $("#bookAuthor").val(title); }); diff --git a/cps/static/js/get_meta.js b/cps/static/js/get_meta.js index 43a40fa6..10fd5835 100644 --- a/cps/static/js/get_meta.js +++ b/cps/static/js/get_meta.js @@ -38,12 +38,12 @@ $(function () { } function populateForm (book) { - tinymce.get("description").setContent(book.description); + tinymce.get("comments").setContent(book.comments); var uniqueTags = getUniqueValues('tags', book) var uniqueLanguages = getUniqueValues('languages', book) var ampSeparatedAuthors = (book.authors || []).join(" & "); $("#bookAuthor").val(ampSeparatedAuthors); - $("#book_title").val(book.title); + $("#title").val(book.title); $("#tags").val(uniqueTags.join(", ")); $("#languages").val(uniqueLanguages.join(", ")); $("#rating").data("rating").setValue(Math.round(book.rating)); @@ -172,7 +172,7 @@ $(function () { $("#get_meta").click(function () { populate_provider(); - var bookTitle = $("#book_title").val(); + var bookTitle = $("#title").val(); $("#keyword").val(bookTitle); keyword = bookTitle; doSearch(bookTitle); diff --git a/cps/templates/author.html b/cps/templates/author.html index f7314586..1e02597c 100644 --- a/cps/templates/author.html +++ b/cps/templates/author.html @@ -62,11 +62,9 @@ {{author.name.replace('|',',')|shortentitle(30)}} {% endif %} {% endfor %} - {% for format in entry.Books.data %} - {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} + {% if entry.Books.data|music %} - {% endif %} - {% endfor %} + {% endif %}

{% if entry.Books.series.__len__() > 0 %}

diff --git a/cps/templates/book_edit.html b/cps/templates/book_edit.html index e3053b2b..94dbbb3b 100644 --- a/cps/templates/book_edit.html +++ b/cps/templates/book_edit.html @@ -68,20 +68,20 @@

- - + +
- +
- - + +
@@ -296,7 +296,7 @@ 'no_result': {{_('No Result(s) found! Please try another keyword.')|safe|tojson}}, 'author': {{_('Author')|safe|tojson}}, 'publisher': {{_('Publisher')|safe|tojson}}, - 'description': {{_('Description')|safe|tojson}}, + 'comments': {{_('Description')|safe|tojson}}, 'source': {{_('Source')|safe|tojson}}, }; var language = '{{ current_user.locale }}'; diff --git a/cps/templates/image.html b/cps/templates/image.html index 0bdba9a5..3234df52 100644 --- a/cps/templates/image.html +++ b/cps/templates/image.html @@ -15,6 +15,6 @@ {{ book_title }} {%- endmacro %} diff --git a/cps/templates/index.html b/cps/templates/index.html index b1169e01..38fbb284 100644 --- a/cps/templates/index.html +++ b/cps/templates/index.html @@ -119,11 +119,9 @@ {{author.name.replace('|',',')|shortentitle(30)}} {% endif %} {% endfor %} - {% for format in entry.Books.data %} - {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} + {% if entry.Books.data|music %} - {% endif %} - {%endfor%} + {% endif %}

{% if entry.Books.series.__len__() > 0 %}

diff --git a/cps/templates/search.html b/cps/templates/search.html index 73e0ffba..9a7af2d8 100644 --- a/cps/templates/search.html +++ b/cps/templates/search.html @@ -73,11 +73,9 @@ {{author.name.replace('|',',')|shortentitle(30)}} {% endif %} {% endfor %} - {% for format in entry.Books.data %} - {% if format.format|lower in g.constants.EXTENSIONS_AUDIO %} + {% if entry.Books.data|music %} - {% endif %} - {% endfor %} + {% endif %}

{% if entry.Books.series.__len__() > 0 %}

diff --git a/cps/templates/search_form.html b/cps/templates/search_form.html index cdce85a5..58585d28 100644 --- a/cps/templates/search_form.html +++ b/cps/templates/search_form.html @@ -5,12 +5,12 @@

- - + +
{% if cc|length > 0 %} diff --git a/cps/web.py b/cps/web.py index 9bd7447e..8277a763 100644 --- a/cps/web.py +++ b/cps/web.py @@ -301,8 +301,8 @@ def get_matching_tags(): q = calibre_db.session.query(db.Books).filter(calibre_db.common_filters(True)) calibre_db.create_functions() # calibre_db.session.connection().connection.connection.create_function("lower", 1, db.lcase) - author_input = request.args.get('author_name') or '' - title_input = request.args.get('book_title') or '' + author_input = request.args.get('authors') or '' + title_input = request.args.get('title') or '' include_tag_inputs = request.args.getlist('include_tag') or '' exclude_tag_inputs = request.args.getlist('exclude_tag') or '' q = q.filter(db.Books.authors.any(func.lower(db.Authors.name).ilike("%" + author_input + "%")),