Implement single click download of metadata and covers for all sleected books in the library. To access click the arrow next to the Edit metadata button. Fixes #2376 (Enhancement request: Additional buttons on main interface)

This commit is contained in:
Kovid Goyal 2009-05-06 19:46:03 -07:00
parent 4f643a17f6
commit ecdb71c88d
2 changed files with 176 additions and 0 deletions

View File

@ -24,6 +24,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
max_available_height, config, info_dialog, \ max_available_height, config, info_dialog, \
available_width, GetMetadata available_width, GetMetadata
from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror from calibre.gui2.cover_flow import CoverFlow, DatabaseImages, pictureflowerror
from calibre.gui2.widgets import ProgressIndicator, WarningDialog
from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.gui2.update import CheckForUpdates from calibre.gui2.update import CheckForUpdates
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
@ -75,6 +76,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
Ui_MainWindow.__init__(self) Ui_MainWindow.__init__(self)
self.setupUi(self) self.setupUi(self)
self.setWindowTitle(__appname__) self.setWindowTitle(__appname__)
self.progress_indicator = ProgressIndicator(self)
self.verbose = opts.verbose self.verbose = opts.verbose
self.get_metadata = GetMetadata() self.get_metadata = GetMetadata()
self.read_settings() self.read_settings()
@ -170,6 +172,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
md.addAction(_('Edit metadata individually')) md.addAction(_('Edit metadata individually'))
md.addSeparator() md.addSeparator()
md.addAction(_('Edit metadata in bulk')) md.addAction(_('Edit metadata in bulk'))
md.addSeparator()
md.addAction(_('Download metadata and covers'))
md.addAction(_('Download only metadata'))
self.metadata_menu = md self.metadata_menu = md
self.add_menu = QMenu() self.add_menu = QMenu()
self.add_menu.addAction(_('Add books from a single directory')) self.add_menu.addAction(_('Add books from a single directory'))
@ -196,6 +201,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
partial(self.edit_metadata, bulk=False)) partial(self.edit_metadata, bulk=False))
QObject.connect(md.actions()[2], SIGNAL('triggered(bool)'), QObject.connect(md.actions()[2], SIGNAL('triggered(bool)'),
partial(self.edit_metadata, bulk=True)) partial(self.edit_metadata, bulk=True))
QObject.connect(md.actions()[4], SIGNAL('triggered(bool)'),
partial(self.download_metadata, covers=True))
QObject.connect(md.actions()[5], SIGNAL('triggered(bool)'),
partial(self.download_metadata, covers=False))
self.save_menu = QMenu() self.save_menu = QMenu()
self.save_menu.addAction(_('Save to disk')) self.save_menu.addAction(_('Save to disk'))
self.save_menu.addAction(_('Save to disk in a single directory')) self.save_menu.addAction(_('Save to disk in a single directory'))
@ -824,6 +835,54 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
############################################################################ ############################################################################
############################### Edit metadata ############################## ############################### Edit metadata ##############################
def download_metadata(self, checked, covers=True):
rows = self.library_view.selectionModel().selectedRows()
previous = self.library_view.currentIndex()
if not rows or len(rows) == 0:
d = error_dialog(self, _('Cannot download metadata'),
_('No books selected'))
d.exec_()
return
db = self.library_view.model().db
ids = [db.id(row.row()) for row in rows]
from calibre.gui2.metadata import DownloadMetadata
self._download_book_metadata = DownloadMetadata(db, ids, get_covers=covers)
self._download_book_metadata.start()
self.progress_indicator.start(
_('Downloading metadata for %d book(s)')%len(ids))
self._book_metadata_download_check = QTimer(self)
self.connect(self._book_metadata_download_check,
SIGNAL('timeout()'), self.book_metadata_download_check)
self._book_metadata_download_check.start(100)
def book_metadata_download_check(self):
if self._download_book_metadata.is_alive():
return
self._book_metadata_download_check.stop()
self.progress_indicator.stop()
cr = self.library_view.currentIndex().row()
x = self._download_book_metadata
self._download_book_metadata = None
if x.exception is None:
db = self.library_view.model().refresh_ids(
x.updated, cr)
if x.failures:
details = ['<li><b>%s:</b> %s</li>'%(title, reason) for title,
reason in x.failures.values()]
details = '<p><ul>%s</ul></p>'%(''.join(details))
WarningDialog(_('Failed to download some metadata'),
_('Failed to download metadata for the following:'),
details, self).exec_()
else:
err = _('<b>Failed to download metadata:')+\
'</b><br><pre>'+x.tb+'</pre>'
ConversionErrorDialog(self, _('Error'), err,
show=True)
def edit_metadata(self, checked, bulk=None): def edit_metadata(self, checked, bulk=None):
''' '''
Edit metadata of selected books in library. Edit metadata of selected books in library.

View File

@ -0,0 +1,117 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
from __future__ import with_statement
__license__ = 'GPL v3'
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from threading import Thread
from Queue import Queue, Empty
from calibre.ebooks.metadata.fetch import search
from calibre.utils.config import prefs
from calibre.ebooks.metadata.library_thing import cover_from_isbn
class Worker(Thread):
def __init__(self):
Thread.__init__(self)
self.setDaemon(True)
self.jobs = Queue()
self.results = Queue()
def run(self):
while True:
isbn = self.jobs.get()
if not isbn:
break
cdata, _ = cover_from_isbn(isbn)
if cdata:
self.results.put((isbn, cdata))
def __enter__(self):
self.start()
return self
def __exit__(self, *args):
self.jobs.put(False)
class DownloadMetadata(Thread):
def __init__(self, db, ids, get_covers):
Thread.__init__(self)
self.setDaemon(True)
self.metadata = {}
self.covers = {}
self.db = db
self.updated = set([])
self.get_covers = get_covers
self.worker = Worker()
for id in ids:
self.metadata[id] = db.get_metadata(id, index_is_id=True)
def run(self):
self.exception = self.tb = None
try:
self._run()
except Exception, e:
self.exception = e
import traceback
self.tb = traceback.format_exc()
def _run(self):
self.key = prefs['isbndb_com_key']
if not self.key:
self.key = None
self.fetched_metadata = {}
self.failures = {}
with self.worker:
for id, mi in self.metadata.items():
args = {}
if mi.isbn:
args['isbn'] = mi.isbn
else:
if not mi.title:
self.failures[id] = \
(str(id), _('Book has neither title nor ISBN'))
continue
args['title'] = mi.title
if mi.authors:
args['author'] = mi.authors[0]
if self.key:
args['isbndb_key'] = self.key
results, exceptions = search(**args)
if results:
fmi = results[0]
self.fetched_metadata[id] = fmi
if fmi.isbn and self.get_covers:
self.worker.jobs.put(fmi.isbn)
mi.smart_update(fmi)
else:
self.failures[id] = (mi.title,
_('No matches found for this book'))
self.commit_covers()
self.commit_covers(True)
for id in self.fetched_metadata:
self.db.set_metadata(id, self.metadata[id])
self.updated = set(self.fetched_metadata)
def commit_covers(self, all=False):
if all:
self.worker.jobs.put(False)
while True:
try:
isbn, cdata = self.worker.results.get(False)
for id, mi in self.metadata.items():
if mi.isbn == isbn:
self.db.set_cover(id, cdata)
except Empty:
if not all or not self.worker.is_alive():
return