Pull from trunk

This commit is contained in:
Kovid Goyal 2009-05-06 19:52:51 -07:00
commit 2ba35a8be8
3 changed files with 192 additions and 9 deletions

View File

@ -12,6 +12,7 @@ from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, QPixmap, \
from calibre.customize.ui import available_input_formats, available_output_formats, \
device_plugins
from calibre.devices.interface import DevicePlugin
from calibre.constants import iswindows
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
from calibre.parallel import Job
@ -27,6 +28,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):
@ -541,7 +547,7 @@ class DeviceGUI(object):
p.loadFromData(data)
if not p.isNull():
ht = self.device_manager.device_class.THUMBNAIL_HEIGHT \
if self.device_manager else Device.THUMBNAIL_HEIGHT
if self.device_manager else DevicePlugin.THUMBNAIL_HEIGHT
p = p.scaledToHeight(ht, Qt.SmoothTransformation)
return (p.width(), p.height(), pixmap_to_data(p))
@ -616,7 +622,7 @@ class DeviceGUI(object):
ids = [self.library_view.model().id(r) for r in self.library_view.selectionModel().selectedRows()] if send_ids is None else send_ids
if not self.device_manager or not ids or len(ids) == 0:
return
_files, _auto_ids = self.library_view.model().get_preferred_formats_from_ids(ids,
self.device_manager.device_class.settings().format_map,
paths=True, set_metadata=True,
@ -626,7 +632,7 @@ class DeviceGUI(object):
ids = list(set(ids).difference(_auto_ids))
else:
_auto_ids = []
metadata = self.library_view.model().get_metadata(ids, True)
ids = iter(ids)
for mi in metadata:
@ -634,7 +640,7 @@ class DeviceGUI(object):
if cdata:
mi['cover'] = self.cover_to_thumbnail(cdata)
metadata = iter(metadata)
files = [getattr(f, 'name', None) for f in _files]
bad, good, gf, names, remove_ids = [], [], [], [], []
for f in files:
@ -660,13 +666,13 @@ class DeviceGUI(object):
remove = remove_ids if delete_from_library else []
self.upload_books(gf, names, good, on_card, memory=(_files, remove))
self.status_bar.showMessage(_('Sending books to device.'), 5000)
auto = []
if _auto_ids != []:
for id in _auto_ids:
if specific_format == None:
formats = [f.lower() for f in self.library_view.model().db.formats(id, index_is_id=True).split(',')]
formats = formats if formats != None else []
formats = formats if formats != None else []
if list(set(formats).intersection(available_input_formats())) != [] and list(set(self.device_manager.device_class.settings().format_map).intersection(available_output_formats())) != []:
auto.append(id)
else:
@ -676,7 +682,7 @@ class DeviceGUI(object):
auto.append(id)
else:
bad.append(self.library_view.model().db.title(id, index_is_id=True))
if auto != []:
format = None
for fmt in self.device_manager.device_class.settings().format_map:

View File

@ -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
@ -74,6 +75,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()
@ -169,6 +171,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'))
@ -195,6 +200,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'))
@ -821,6 +832,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 = ['<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):
'''
Edit metadata of selected books in library.
@ -1081,8 +1140,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:

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