Cover download: Allowing saving alternate covers to disk or in the book's data folder. Fixes #2020603 [[Enhancement] Cover Image Downloader: Save cover to disk/data folder](https://bugs.launchpad.net/calibre/+bug/2020603)

This commit is contained in:
Kovid Goyal 2023-05-24 08:23:22 +05:30
parent 924ba7adfe
commit 4ecd1a2b7d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -24,16 +24,20 @@ from threading import Event, Thread
from calibre import force_unicode from calibre import force_unicode
from calibre.customize.ui import metadata_plugins from calibre.customize.ui import metadata_plugins
from calibre.db.constants import COVER_FILE_NAME, DATA_DIR_NAME
from calibre.ebooks.metadata import authors_to_string, rating_to_stars from calibre.ebooks.metadata import authors_to_string, rating_to_stars
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
from calibre.gui2 import error_dialog, gprefs, rating_font from calibre.gui2 import (
choose_save_file, error_dialog, gprefs, rating_font,
)
from calibre.gui2.progress_indicator import SpinAnimator from calibre.gui2.progress_indicator import SpinAnimator
from calibre.gui2.widgets2 import HTMLDisplay from calibre.gui2.widgets2 import HTMLDisplay
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
from calibre.ptempfile import TemporaryDirectory from calibre.ptempfile import TemporaryDirectory
from calibre.utils.date import UNDEFINED_DATE, as_utc, format_date, fromordinal, utcnow from calibre.utils.date import UNDEFINED_DATE, as_utc, format_date, fromordinal, utcnow
from calibre.utils.img import image_to_data, save_image
from calibre.utils.ipc.simple_worker import WorkerError, fork_job from calibre.utils.ipc.simple_worker import WorkerError, fork_job
from calibre.utils.logging import GUILog as Log from calibre.utils.logging import GUILog as Log
from calibre.utils.resources import get_image_path as I from calibre.utils.resources import get_image_path as I
@ -772,6 +776,7 @@ class CoversView(QListView): # {{{
def __init__(self, current_cover, parent=None): def __init__(self, current_cover, parent=None):
QListView.__init__(self, parent) QListView.__init__(self, parent)
self.book_id = getattr(parent, 'book_id', 0)
self.m = CoversModel(current_cover, self) self.m = CoversModel(current_cover, self)
self.setModel(self.m) self.setModel(self.m)
@ -828,26 +833,58 @@ class CoversView(QListView): # {{{
m = QMenu(self) m = QMenu(self)
m.addAction(QIcon.ic('view.png'), _('View this cover at full size'), self.show_cover) m.addAction(QIcon.ic('view.png'), _('View this cover at full size'), self.show_cover)
m.addAction(QIcon.ic('edit-copy.png'), _('Copy this cover to clipboard'), self.copy_cover) m.addAction(QIcon.ic('edit-copy.png'), _('Copy this cover to clipboard'), self.copy_cover)
m.addAction(QIcon.ic('save.png'), _('Save this cover to disk'), self.save_to_disk)
if self.book_id:
m.addAction(QIcon.ic('save.png'), _('Save this cover as in the book extra files'), self.save_alternate_cover)
m.exec(QCursor.pos()) m.exec(QCursor.pos())
def show_cover(self): @property
def current_pixmap(self):
idx = self.currentIndex() idx = self.currentIndex()
pmap = self.model().cover_pixmap(idx) pmap = self.model().cover_pixmap(idx)
if pmap is None and idx.row() == 0: if pmap is None and idx.row() == 0:
pmap = self.model().cc pmap = self.model().cc
return pmap
def show_cover(self):
pmap = self.current_pixmap
if pmap is not None: if pmap is not None:
from calibre.gui2.image_popup import ImageView from calibre.gui2.image_popup import ImageView
d = ImageView(self, pmap, str(idx.data(Qt.ItemDataRole.DisplayRole) or ''), geom_name='metadata_download_cover_popup_geom') d = ImageView(self, pmap, str(self.currentIndex().data(Qt.ItemDataRole.DisplayRole) or ''), geom_name='metadata_download_cover_popup_geom')
d(use_exec=True) d(use_exec=True)
def copy_cover(self): def copy_cover(self):
idx = self.currentIndex() pmap = self.current_pixmap
pmap = self.model().cover_pixmap(idx)
if pmap is None and idx.row() == 0:
pmap = self.model().cc
if pmap is not None: if pmap is not None:
QApplication.clipboard().setPixmap(pmap) QApplication.clipboard().setPixmap(pmap)
def save_to_disk(self):
pmap = self.current_pixmap
if pmap:
path = choose_save_file(self, 'save-downloaded-cover-to-disk', _('Save cover image'), filters=[
(_('Images'), ['jpg', 'jpeg', 'png', 'webp'])], all_files=False, initial_filename=COVER_FILE_NAME)
if path:
save_image(pmap.toImage(), path)
def save_alternate_cover(self):
pmap = self.current_pixmap
if pmap:
from calibre.gui2.ui import get_gui
db = get_gui().current_db.new_api
existing = {x[0] for x in db.list_extra_files(self.book_id)}
h, ext = os.path.splitext(COVER_FILE_NAME)
template = f'{DATA_DIR_NAME}/{h}-{{:03d}}{ext}'
for i in range(1, 1000):
q = template.format(i)
if q not in existing:
cdata = image_to_data(pmap.toImage())
db.add_extra_files(self.book_id, {q: BytesIO(cdata)}, replace=False, auto_rename=True)
break
else:
error_dialog(self, _('Too many covers'), _(
'Could not save cover as there are too many existing covers'), show=True)
return
def keyPressEvent(self, ev): def keyPressEvent(self, ev):
if ev.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return): if ev.key() in (Qt.Key.Key_Enter, Qt.Key.Key_Return):
self.chosen.emit() self.chosen.emit()
@ -873,6 +910,7 @@ class CoversWidget(QWidget): # {{{
self.msg = QLabel() self.msg = QLabel()
self.msg.setWordWrap(True) self.msg.setWordWrap(True)
self.book_id = getattr(parent, 'book_id', 0)
l.addWidget(self.msg, 0, 0) l.addWidget(self.msg, 0, 0)
self.covers_view = CoversView(current_cover, self) self.covers_view = CoversView(current_cover, self)
@ -1024,6 +1062,7 @@ class FullFetch(QDialog): # {{{
def __init__(self, current_cover=None, parent=None): def __init__(self, current_cover=None, parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
self.current_cover = current_cover self.current_cover = current_cover
self.book_id = getattr(parent, 'book_id', 0)
self.log = Log() self.log = Log()
self.book = self.cover_pixmap = None self.book = self.cover_pixmap = None
@ -1138,6 +1177,7 @@ class CoverFetch(QDialog): # {{{
def __init__(self, current_cover=None, parent=None): def __init__(self, current_cover=None, parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
self.current_cover = current_cover self.current_cover = current_cover
self.book_id = getattr(parent, 'book_id', 0)
self.log = Log() self.log = Log()
self.cover_pixmap = None self.cover_pixmap = None