From 2d000bf23eea8ca54150c1d2ee1b8be2bbeb72c1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 2 Oct 2023 15:44:11 +0530 Subject: [PATCH] Make merge_metadata a db API Since it operates on the database and is a fairly small function, no sense in having it in a separate module --- src/calibre/db/cache.py | 94 ++++++++++++++++++ .../metadata/book/merge_book_metadata.py | 97 ------------------- src/calibre/gui2/actions/edit_metadata.py | 4 +- 3 files changed, 95 insertions(+), 100 deletions(-) delete mode 100644 src/calibre/ebooks/metadata/book/merge_book_metadata.py diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index bdd5214026..54652308aa 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -3165,6 +3165,100 @@ class Cache: path = self._field_for('path', book_id).replace('/', os.sep) self.backend.copy_extra_file_to(book_id, path, relpath, stream_or_path) + @write_api + def merge_book_metadata(self, dest_id, src_ids, replace_cover=False): + dest_mi = self.get_metadata(dest_id) + merged_identifiers = self._field_for('identifiers', dest_id) or {} + orig_dest_comments = dest_mi.comments + dest_cover = orig_dest_cover = self.cover(dest_id) + had_orig_cover = bool(dest_cover) + from calibre.utils.date import is_date_undefined + + def is_null_date(x): + return x is None or is_date_undefined(x) + + for src_id in src_ids: + src_mi = self.get_metadata(src_id) + + if src_mi.comments and orig_dest_comments != src_mi.comments: + if not dest_mi.comments: + dest_mi.comments = src_mi.comments + else: + dest_mi.comments = str(dest_mi.comments) + '\n\n' + str(src_mi.comments) + if src_mi.title and dest_mi.is_null('title'): + dest_mi.title = src_mi.title + dest_mi.title_sort = src_mi.title_sort + if (src_mi.authors and src_mi.authors[0] != _('Unknown')) and (not dest_mi.authors or dest_mi.authors[0] == _('Unknown')): + dest_mi.authors = src_mi.authors + dest_mi.author_sort = src_mi.author_sort + if src_mi.tags: + if not dest_mi.tags: + dest_mi.tags = src_mi.tags + else: + dest_mi.tags.extend(src_mi.tags) + if not dest_cover or replace_cover: + src_cover = self.cover(src_id) + if src_cover: + dest_cover = src_cover + replace_cover = False + if not dest_mi.publisher: + dest_mi.publisher = src_mi.publisher + if not dest_mi.rating: + dest_mi.rating = src_mi.rating + if not dest_mi.series: + dest_mi.series = src_mi.series + dest_mi.series_index = src_mi.series_index + if is_null_date(dest_mi.pubdate) and not is_null_date(src_mi.pubdate): + dest_mi.pubdate = src_mi.pubdate + + src_identifiers = self.field_for('identifier', src_id) or {} + src_identifiers.update(merged_identifiers) + merged_identifiers = src_identifiers.copy() + + if merged_identifiers: + dest_mi.set_identifiers(merged_identifiers) + self._set_metadata(dest_id, dest_mi, ignore_errors=False) + + if dest_cover and (not had_orig_cover or dest_cover is not orig_dest_cover): + self._set_cover({dest_id: dest_cover}) + + for key in self.field_metadata: # loop thru all defined fields + fm = self.field_metadata[key] + if not fm['is_custom']: + continue + dt = fm['datatype'] + label = fm['label'] + try: + field = self.field_metadata.label_to_key(label) + except ValueError: + continue + # Get orig_dest_comments before it gets changed + if dt == 'comments': + orig_dest_value = self._field_for(field, dest_id) + + for src_id in src_ids: + dest_value = self._field_for(field, dest_id) + src_value = self._field_for(field, src_id) + if (dt == 'comments' and src_value and src_value != orig_dest_value): + if not dest_value: + self._set_field(field, {dest_id: src_value}) + else: + dest_value = str(dest_value) + '\n\n' + str(src_value) + self._set_field(field, {dest_id: dest_value}) + if (dt in {'bool', 'int', 'float', 'rating', 'datetime'} and dest_value is None): + self._set_field(field, {dest_id: src_value}) + if (dt == 'series' and not dest_value and src_value): + src_index = self._field_for(field + '_index', src_id) + self._set_field(field, {dest_id:src_value}) + self._set_field(field + '_index', {dest_id:src_index}) + if ((dt == 'enumeration' or (dt == 'text' and not fm['is_multiple'])) and not dest_value): + self._set_field(field, {dest_id:src_value}) + if (dt == 'text' and fm['is_multiple'] and src_value): + if not dest_value: + dest_value = src_value + else: + dest_value.extend(src_value) + self._set_field(field, {dest_id: dest_value}) def import_library(library_key, importer, library_path, progress=None, abort=None): from calibre.db.backend import DB diff --git a/src/calibre/ebooks/metadata/book/merge_book_metadata.py b/src/calibre/ebooks/metadata/book/merge_book_metadata.py deleted file mode 100644 index c72a4ea8d1..0000000000 --- a/src/calibre/ebooks/metadata/book/merge_book_metadata.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python - - -__license__ = 'GPL v3' -__copyright__ = '2010, Kovid Goyal ' -__docformat__ = 'restructuredtext en' - -from calibre.utils.date import is_date_undefined - - -def merge_book_metadata(db, dest_id, src_ids, replace_cover=False): - dest_mi = db.get_metadata(dest_id, index_is_id=True) - merged_identifiers = db.get_identifiers(dest_id, index_is_id=True) - orig_dest_comments = dest_mi.comments - dest_cover = orig_dest_cover = db.cover(dest_id, index_is_id=True) - had_orig_cover = bool(dest_cover) - - def is_null_date(x): - return x is None or is_date_undefined(x) - - for src_id in src_ids: - src_mi = db.get_metadata(src_id, index_is_id=True) - - if src_mi.comments and orig_dest_comments != src_mi.comments: - if not dest_mi.comments: - dest_mi.comments = src_mi.comments - else: - dest_mi.comments = str(dest_mi.comments) + '\n\n' + str(src_mi.comments) - if src_mi.title and dest_mi.is_null('title'): - dest_mi.title = src_mi.title - dest_mi.title_sort = src_mi.title_sort - if (src_mi.authors and src_mi.authors[0] != _('Unknown')) and (not dest_mi.authors or dest_mi.authors[0] == _('Unknown')): - dest_mi.authors = src_mi.authors - dest_mi.author_sort = src_mi.author_sort - if src_mi.tags: - if not dest_mi.tags: - dest_mi.tags = src_mi.tags - else: - dest_mi.tags.extend(src_mi.tags) - if not dest_cover or replace_cover: - src_cover = db.cover(src_id, index_is_id=True) - if src_cover: - dest_cover = src_cover - replace_cover = False - if not dest_mi.publisher: - dest_mi.publisher = src_mi.publisher - if not dest_mi.rating: - dest_mi.rating = src_mi.rating - if not dest_mi.series: - dest_mi.series = src_mi.series - dest_mi.series_index = src_mi.series_index - if is_null_date(dest_mi.pubdate) and not is_null_date(src_mi.pubdate): - dest_mi.pubdate = src_mi.pubdate - - src_identifiers = db.get_identifiers(src_id, index_is_id=True) - src_identifiers.update(merged_identifiers) - merged_identifiers = src_identifiers.copy() - - if merged_identifiers: - dest_mi.set_identifiers(merged_identifiers) - db.set_metadata(dest_id, dest_mi, ignore_errors=False) - - if dest_cover and (not had_orig_cover or dest_cover is not orig_dest_cover): - db.set_cover(dest_id, dest_cover) - - for key in db.field_metadata: # loop thru all defined fields - fm = db.field_metadata[key] - if not fm['is_custom']: - continue - dt = fm['datatype'] - colnum = fm['colnum'] - # Get orig_dest_comments before it gets changed - if dt == 'comments': - orig_dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True) - - for src_id in src_ids: - dest_value = db.get_custom(dest_id, num=colnum, index_is_id=True) - src_value = db.get_custom(src_id, num=colnum, index_is_id=True) - if (dt == 'comments' and src_value and src_value != orig_dest_value): - if not dest_value: - db.set_custom(dest_id, src_value, num=colnum) - else: - dest_value = str(dest_value) + '\n\n' + str(src_value) - db.set_custom(dest_id, dest_value, num=colnum) - if (dt in {'bool', 'int', 'float', 'rating', 'datetime'} and dest_value is None): - db.set_custom(dest_id, src_value, num=colnum) - if (dt == 'series' and not dest_value and src_value): - src_index = db.get_custom_extra(src_id, num=colnum, index_is_id=True) - db.set_custom(dest_id, src_value, num=colnum, extra=src_index) - if ((dt == 'enumeration' or (dt == 'text' and not fm['is_multiple'])) and not dest_value): - db.set_custom(dest_id, src_value, num=colnum) - if (dt == 'text' and fm['is_multiple'] and src_value): - if not dest_value: - dest_value = src_value - else: - dest_value.extend(src_value) - db.set_custom(dest_id, dest_value, num=colnum) \ No newline at end of file diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index 099024e697..388b71d902 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -18,7 +18,6 @@ from qt.core import ( from calibre.db.errors import NoSuchFormat from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata.book.base import Metadata -from calibre.ebooks.metadata.book.merge_book_metadata import merge_book_metadata from calibre.ebooks.metadata.opf2 import OPF, metadata_to_opf from calibre.ebooks.metadata.sources.prefs import msprefs from calibre.gui2 import Dispatcher, error_dialog, gprefs, question_dialog @@ -732,8 +731,7 @@ class EditMetadataAction(InterfaceAction): self.gui.library_view.model().delete_books_by_id(ids_to_delete) def merge_metadata(self, dest_id, src_ids, replace_cover=False): - db = self.gui.library_view.model().db - merge_book_metadata(db, dest_id, src_ids, replace_cover) + self.gui.current_db.new_api.merge_book_metadata(dest_id, src_ids, replace_cover) # }}} def edit_device_collections(self, view, oncard=None):