From 3523ecaf5ffb76ac5460f717753586e6ff9b64e7 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 6 May 2009 13:43:01 -0700
Subject: [PATCH 1/3] Fix #2402 (Get error when I plug in my kindle 2)
---
src/calibre/gui2/main.py | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py
index 09f95033f1..dd8875984d 100644
--- a/src/calibre/gui2/main.py
+++ b/src/calibre/gui2/main.py
@@ -1060,8 +1060,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
#############################View book######################################
def view_format(self, row, format):
- self._view_file(self.library_view.model().db.format(row,
- format, as_file=True).name)
+ fmt_path = self.library_view.model().db.format_abspath(row, format)
+ if fmt_path:
+ self._view_file(fmt_path)
def book_downloaded_for_viewing(self, job):
if job.exception:
From 4f643a17f680b4aeecb45656ae0b0a57e12bd53a Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 6 May 2009 15:19:30 -0700
Subject: [PATCH 2/3] Fix #2390 (Error window when a large number books cannot
be transferred to a device because of incompatible formats is too large for
dis)
---
src/calibre/gui2/device.py | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py
index 61869cce34..a29666090b 100644
--- a/src/calibre/gui2/device.py
+++ b/src/calibre/gui2/device.py
@@ -16,7 +16,7 @@ from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.parallel import Job
from calibre.devices.scanner import DeviceScanner
from calibre.gui2 import config, error_dialog, Dispatcher, dynamic, \
- pixmap_to_data, warning_dialog
+ pixmap_to_data
from calibre.ebooks.metadata import authors_to_string
from calibre.gui2.dialogs.conversion_error import ConversionErrorDialog
from calibre.devices.interface import Device
@@ -26,6 +26,11 @@ from calibre.devices.errors import FreeSpaceError
from calibre.utils.smtp import compose_mail, sendmail, extract_email_address, \
config as email_config
+def warning(title, msg, details, parent):
+ from calibre.gui2.widgets import WarningDialog
+ WarningDialog(title, msg, details, parent).exec_()
+
+
class DeviceJob(Job):
def __init__(self, func, *args, **kwargs):
@@ -478,11 +483,11 @@ class DeviceGUI(object):
self.status_bar.showMessage(_('Sending email to')+' '+to, 3000)
if bad:
- bad = '\n'.join('%s'%(i,) for i in bad)
- d = warning_dialog(self, _('No suitable formats'),
- ''+ _('Could not email the following books '
- 'as no suitable formats were found:
')%(bad,))
- d.exec_()
+ bad = u'\n'.join(u'%s'%(i,) for i in bad)
+ details = u'
'%bad
+ warning(_('No suitable formats'),
+ _('Could not email the following books '
+ 'as no suitable formats were found:'), details, self)
def emails_sent(self, results, remove=[]):
errors, good = [], []
@@ -624,13 +629,13 @@ class DeviceGUI(object):
self.upload_books(gf, names, good, on_card, memory=(_files, remove))
self.status_bar.showMessage(_('Sending books to device.'), 5000)
if bad:
- bad = '\n'.join('%s'%(i,) for i in bad)
- d = warning_dialog(self, _('No suitable formats'),
+ bad = u'\n'.join(u'%s'%(i,) for i in bad)
+ details = u''%bad
+ warning(_('No suitable formats'),
_('Could not upload the following books to the device, '
'as no suitable formats were found. Try changing the output '
'format in the upper right corner next to the red heart and '
- 're-converting.
')%(bad,))
- d.exec_()
+ 're-converting.'), details, self)
def upload_booklists(self):
'''
From ecdb71c88d049c0c131f22c789343d2d9a150453 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Wed, 6 May 2009 19:46:03 -0700
Subject: [PATCH 3/3] 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)
---
src/calibre/gui2/main.py | 59 ++++++++++++++++++
src/calibre/gui2/metadata.py | 117 +++++++++++++++++++++++++++++++++++
2 files changed, 176 insertions(+)
create mode 100644 src/calibre/gui2/metadata.py
diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py
index dd8875984d..6d7fd63378 100644
--- a/src/calibre/gui2/main.py
+++ b/src/calibre/gui2/main.py
@@ -24,6 +24,7 @@ from calibre.gui2 import APP_UID, warning_dialog, choose_files, error_dialog, \
max_available_height, config, info_dialog, \
available_width, GetMetadata
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.update import CheckForUpdates
from calibre.gui2.dialogs.progress import ProgressDialog
@@ -75,6 +76,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.setWindowTitle(__appname__)
+ self.progress_indicator = ProgressIndicator(self)
self.verbose = opts.verbose
self.get_metadata = GetMetadata()
self.read_settings()
@@ -170,6 +172,9 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
md.addAction(_('Edit metadata individually'))
md.addSeparator()
md.addAction(_('Edit metadata in bulk'))
+ md.addSeparator()
+ md.addAction(_('Download metadata and covers'))
+ md.addAction(_('Download only metadata'))
self.metadata_menu = md
self.add_menu = QMenu()
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))
QObject.connect(md.actions()[2], SIGNAL('triggered(bool)'),
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.addAction(_('Save to disk'))
self.save_menu.addAction(_('Save to disk in a single directory'))
@@ -824,6 +835,54 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
############################################################################
############################### 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 = ['%s: %s'%(title, reason) for title,
+ reason in x.failures.values()]
+ details = ''%(''.join(details))
+ WarningDialog(_('Failed to download some metadata'),
+ _('Failed to download metadata for the following:'),
+ details, self).exec_()
+ else:
+ err = _('Failed to download metadata:')+\
+ '
'+x.tb+'
'
+ ConversionErrorDialog(self, _('Error'), err,
+ show=True)
+
+
+
+
def edit_metadata(self, checked, bulk=None):
'''
Edit metadata of selected books in library.
diff --git a/src/calibre/gui2/metadata.py b/src/calibre/gui2/metadata.py
new file mode 100644
index 0000000000..607fd6a41b
--- /dev/null
+++ b/src/calibre/gui2/metadata.py
@@ -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 '
+__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
+
+