mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
924ba7adfe
commit
4ecd1a2b7d
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user