mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
When merging books by drag-and-drop add an option to use the dragged cover instead of the cover in the target book. Fixes #2027794 [Merging books keeps metadata of first book but the cover of the of the second book](https://bugs.launchpad.net/calibre/+bug/2027794)
This commit is contained in:
parent
df7a0b3da3
commit
2ae0742c98
@ -588,22 +588,27 @@ class EditMetadataAction(InterfaceAction):
|
||||
'merge_too_many_books', self.gui)
|
||||
|
||||
def books_dropped(self, merge_map):
|
||||
covers_replaced = False
|
||||
for dest_id, src_ids in iteritems(merge_map):
|
||||
if not self.confirm_large_merge(len(src_ids) + 1):
|
||||
continue
|
||||
from calibre.gui2.dialogs.confirm_merge import merge_drop
|
||||
merge_metadata, merge_formats, delete_books = merge_drop(dest_id, src_ids, self.gui)
|
||||
if merge_metadata is None:
|
||||
d = merge_drop(dest_id, src_ids, self.gui)
|
||||
if d is None:
|
||||
return
|
||||
if merge_formats:
|
||||
if d.merge_formats:
|
||||
self.add_formats(dest_id, self.formats_for_ids(list(src_ids)))
|
||||
if merge_metadata:
|
||||
self.merge_metadata(dest_id, src_ids)
|
||||
if delete_books:
|
||||
if d.merge_metadata:
|
||||
self.merge_metadata(dest_id, src_ids, replace_cover=d.replace_cover)
|
||||
if d.replace_cover:
|
||||
covers_replaced = True
|
||||
if d.delete_books:
|
||||
self.delete_books_after_merge(src_ids)
|
||||
# leave the selection highlight on the target book
|
||||
row = self.gui.library_view.ids_to_rows([dest_id])[dest_id]
|
||||
self.gui.library_view.set_current_row(row)
|
||||
if covers_replaced:
|
||||
self.gui.refresh_cover_browser()
|
||||
|
||||
def merge_books(self, safe_merge=False, merge_only_formats=False):
|
||||
'''
|
||||
@ -724,12 +729,12 @@ class EditMetadataAction(InterfaceAction):
|
||||
def delete_books_after_merge(self, ids_to_delete):
|
||||
self.gui.library_view.model().delete_books_by_id(ids_to_delete)
|
||||
|
||||
def merge_metadata(self, dest_id, src_ids):
|
||||
def merge_metadata(self, dest_id, src_ids, replace_cover=False):
|
||||
db = self.gui.library_view.model().db
|
||||
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 = db.cover(dest_id, index_is_id=True)
|
||||
dest_cover = orig_dest_cover = db.cover(dest_id, index_is_id=True)
|
||||
had_orig_cover = bool(dest_cover)
|
||||
|
||||
def is_null_date(x):
|
||||
@ -754,10 +759,11 @@ class EditMetadataAction(InterfaceAction):
|
||||
dest_mi.tags = src_mi.tags
|
||||
else:
|
||||
dest_mi.tags.extend(src_mi.tags)
|
||||
if not dest_cover:
|
||||
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:
|
||||
@ -776,7 +782,7 @@ class EditMetadataAction(InterfaceAction):
|
||||
dest_mi.set_identifiers(merged_identifiers)
|
||||
db.set_metadata(dest_id, dest_mi, ignore_errors=False)
|
||||
|
||||
if not had_orig_cover and dest_cover:
|
||||
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
|
||||
|
@ -4,6 +4,8 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
from qt.core import (
|
||||
QCheckBox, QDialog, QDialogButtonBox, QLabel, QSplitter, Qt, QTextBrowser,
|
||||
QVBoxLayout, QWidget,
|
||||
@ -13,7 +15,7 @@ from calibre.ebooks.metadata import authors_to_string
|
||||
from calibre.ebooks.metadata.book.base import field_metadata
|
||||
from calibre.gui2 import dynamic, gprefs
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm_config_name
|
||||
from calibre.gui2.widgets2 import Dialog
|
||||
from calibre.gui2.widgets2 import Dialog, FlowLayout
|
||||
from calibre.startup import connect_lambda
|
||||
from calibre.utils.config import tweaks
|
||||
from calibre.utils.date import format_date
|
||||
@ -35,10 +37,12 @@ class Target(QTextBrowser):
|
||||
<tr><td>{fm[pubdate][name]}:</td><td>{published}</td></tr>
|
||||
<tr><td>{fm[formats][name]}:</td><td>{formats}</td></tr>
|
||||
<tr><td>{fm[series][name]}:</td><td>{series}</td></tr>
|
||||
<tr><td>{has_cover_title}:</td><td>{has_cover}</td></tr>
|
||||
</table>
|
||||
'''.format(
|
||||
mb=_('Target book'),
|
||||
title=mi.title,
|
||||
has_cover_title=_('Has cover'), has_cover=_('Yes') if mi.has_cover else _('No'),
|
||||
authors=authors_to_string(mi.authors),
|
||||
date=format_date(mi.timestamp, tweaks['gui_timestamp_display_format']), fm=fm,
|
||||
published=(format_date(mi.pubdate, tweaks['gui_pubdate_display_format']) if mi.pubdate else ''),
|
||||
@ -112,10 +116,12 @@ class ChooseMerge(Dialog):
|
||||
s.addWidget(w)
|
||||
w.l = l = QVBoxLayout(w)
|
||||
l.setContentsMargins(0, 0, 0, 0)
|
||||
w.fl = fl = FlowLayout()
|
||||
l.addLayout(fl)
|
||||
|
||||
def cb(name, text, tt=''):
|
||||
ans = QCheckBox(text)
|
||||
l.addWidget(ans)
|
||||
fl.addWidget(ans)
|
||||
prefs_key = ans.prefs_key = 'choose-merge-cb-' + name
|
||||
ans.setChecked(gprefs.get(prefs_key, True))
|
||||
connect_lambda(ans.stateChanged, self, lambda self, state: self.state_changed(getattr(self, name), state), type=Qt.ConnectionType.QueuedConnection)
|
||||
@ -130,6 +136,8 @@ class ChooseMerge(Dialog):
|
||||
'Merge the book files of the selected books into the target book'))
|
||||
cb('delete_books', _('Delete merged books'), _(
|
||||
'Delete the selected books after merging'))
|
||||
cb('replace_cover', _('Replace existing cover'), _(
|
||||
'Replace the cover in the target book with the dragged cover'))
|
||||
l.addStretch(10)
|
||||
self.msg = la = QLabel(self)
|
||||
la.setWordWrap(True)
|
||||
@ -151,21 +159,26 @@ class ChooseMerge(Dialog):
|
||||
mm = self.merge_metadata.isChecked()
|
||||
mf = self.merge_formats.isChecked()
|
||||
rm = self.delete_books.isChecked()
|
||||
rc = self.replace_cover.isChecked()
|
||||
msg = '<p>'
|
||||
if mm and mf:
|
||||
msg += _(
|
||||
'Book formats and metadata from the selected books'
|
||||
' will be merged into the target book ({title}).')
|
||||
if rc or not self.mi.has_cover:
|
||||
msg += ' ' + _('The dragged cover will be used.')
|
||||
elif mf:
|
||||
msg += _('Book formats from the selected books '
|
||||
'will be merged into to the target book ({title}).'
|
||||
' Metadata in the target book will not be changed.')
|
||||
' Metadata and cover in the target book will not be changed.')
|
||||
elif mm:
|
||||
msg += _('Metadata from the selected books '
|
||||
'will be merged into to the target book ({title}).'
|
||||
' Formats will not be merged.')
|
||||
if rc or not self.mi.has_cover:
|
||||
msg += ' ' + _('The dragged cover will be used.')
|
||||
msg += '<br>'
|
||||
msg += _('All book formats of the first selected book will be kept.') + '<br><br>'
|
||||
msg += _('All book formats of the target book will be kept.') + '<br><br>'
|
||||
if rm:
|
||||
msg += _('After being merged, the selected books will be <b>deleted</b>.')
|
||||
if mf:
|
||||
@ -185,11 +198,21 @@ class ChooseMerge(Dialog):
|
||||
|
||||
@property
|
||||
def merge_type(self):
|
||||
return self.merge_metadata.isChecked(), self.merge_formats.isChecked(), self.delete_books.isChecked()
|
||||
return MergeData(
|
||||
self.merge_metadata.isChecked(), self.merge_formats.isChecked(), self.delete_books.isChecked(),
|
||||
self.replace_cover.isChecked(),
|
||||
)
|
||||
|
||||
|
||||
class MergeData(NamedTuple):
|
||||
merge_metadata: bool = False
|
||||
merge_formats: bool = False
|
||||
delete_books: bool = False
|
||||
replace_cover: bool = False
|
||||
|
||||
|
||||
def merge_drop(dest_id, src_ids, gui):
|
||||
d = ChooseMerge(dest_id, src_ids, gui)
|
||||
if d.exec() != QDialog.DialogCode.Accepted:
|
||||
return None, None, None
|
||||
return None
|
||||
return d.merge_type
|
||||
|
Loading…
x
Reference in New Issue
Block a user