Allow single click fetching of covers in the Edit metadata dialog. Now calibre will automatically try to get the ISBN needed to fetch the cover based on title and author of the book.

This commit is contained in:
Kovid Goyal 2009-04-15 13:36:37 -07:00
parent 332dbf4444
commit c2b79fe5d9
4 changed files with 102 additions and 62 deletions

View File

@ -25,7 +25,7 @@ def get_metadata(stream):
for item in litfile.manifest.values(): for item in litfile.manifest.values():
if item.path in candidates: if item.path in candidates:
try: try:
covers.append((litfile.get_file('/data/'+item.internal), covers.append((litfile.get_file('/data/'+item.internal),
ctype)) ctype))
except: except:
pass pass
@ -33,7 +33,7 @@ def get_metadata(stream):
covers.sort(cmp=lambda x, y:cmp(len(x[0]), len(y[0])), reverse=True) covers.sort(cmp=lambda x, y:cmp(len(x[0]), len(y[0])), reverse=True)
idx = 0 idx = 0
if len(covers) > 1: if len(covers) > 1:
if covers[1][1] == covers[1][0]+'-standard': if covers[1][1] == covers[0][1]+'-standard':
idx = 1 idx = 1
mi.cover_data = ('jpg', covers[idx][0]) mi.cover_data = ('jpg', covers[idx][0])
return mi return mi

View File

@ -69,6 +69,8 @@ def _config():
'clicked')) 'clicked'))
c.add_opt('show_donate_button', default=True, c.add_opt('show_donate_button', default=True,
help='Show donation button') help='Show donation button')
c.add_opt('asked_library_thing_password', default=False,
help='Asked library thing password at least once.')
return ConfigProxy(c) return ConfigProxy(c)
config = _config() config = _config()

View File

@ -25,24 +25,47 @@ from calibre import islinux
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.utils.config import prefs from calibre.utils.config import prefs
from calibre.customize.ui import run_plugins_on_import from calibre.customize.ui import run_plugins_on_import
from calibre.gui2 import config as gui_conf
class CoverFetcher(QThread): class CoverFetcher(QThread):
def __init__(self, username, password, isbn, timeout): def __init__(self, username, password, isbn, timeout, title, author):
self.username = username self.username = username
self.password = password self.password = password
self.timeout = timeout self.timeout = timeout
self.isbn = isbn self.isbn = isbn
self.title = title
self.needs_isbn = False
self.author = author
QThread.__init__(self) QThread.__init__(self)
self.exception = self.traceback = self.cover_data = None self.exception = self.traceback = self.cover_data = None
def run(self): def run(self):
try: try:
if not self.isbn:
from calibre.ebooks.metadata.fetch import search
if not self.title:
self.needs_isbn = True
return
au = self.author if self.author else None
key = prefs['isbndb_com_key']
if not key:
key = None
results = search(title=self.title, author=au,
isbndb_key=key)[0]
results = sorted([x.isbn for x in results if x.isbn],
cmp=lambda x,y:cmp(len(x),len(y)), reverse=True)
if not results:
self.needs_isbn = True
return
self.isbn = results[0]
login(self.username, self.password, force=False) login(self.username, self.password, force=False)
self.cover_data = cover_from_isbn(self.isbn, timeout=self.timeout)[0] self.cover_data = cover_from_isbn(self.isbn, timeout=self.timeout)[0]
except Exception, e: except Exception, e:
self.exception = e self.exception = e
self.traceback = traceback.format_exc() self.traceback = traceback.format_exc()
print self.traceback
@ -64,6 +87,8 @@ class AuthorCompleter(QCompleter):
class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
COVER_FETCH_TIMEOUT = 240 # seconds
def do_reset_cover(self, *args): def do_reset_cover(self, *args):
pix = QPixmap(':/images/book.svg') pix = QPixmap(':/images/book.svg')
self.cover.setPixmap(pix) self.cover.setPixmap(pix)
@ -345,36 +370,39 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
def lt_password_dialog(self): def lt_password_dialog(self):
return PasswordDialog(self, 'LibraryThing account', return PasswordDialog(self, 'LibraryThing account',
_('<p>Enter your username and password for <b>LibraryThing.com</b>. <br/>If you do not have one, you can <a href=\'http://www.librarything.com\'>register</a> for free!.</p>')) _('<p>Enter your username and password for '
'<b>LibraryThing.com</b>. This is <b>optional</b>. It will '
'make fetching of covers faster and more reliable.<br/>If '
'you do not have an account, you can '
'<a href=\'http://www.librarything.com\'>register</a> for '
'free.</p>'))
def change_password(self): def change_password(self):
d = self.lt_password_dialog() d = self.lt_password_dialog()
d.exec_() d.exec_()
def fetch_cover(self): def fetch_cover(self):
isbn = qstring_to_unicode(self.isbn.text()) isbn = unicode(self.isbn.text()).strip()
if isbn: d = self.lt_password_dialog()
d = self.lt_password_dialog() if not gui_conf['asked_library_thing_password'] and \
if not d.username() or not d.password(): (not d.username() or not d.password()):
d.exec_() d.exec_()
if d.result() != PasswordDialog.Accepted: gui_conf['asked_library_thing_password'] = True
return self.fetch_cover_button.setEnabled(False)
self.fetch_cover_button.setEnabled(False) self.setCursor(Qt.WaitCursor)
self.setCursor(Qt.WaitCursor) title, author = map(unicode, (self.title.text(), self.authors.text()))
self.cover_fetcher = CoverFetcher(d.username(), d.password(), isbn, self.cover_fetcher = CoverFetcher(d.username(), d.password(), isbn,
self.timeout) self.timeout, title, author)
self.cover_fetcher.start() self.cover_fetcher.start()
self._hangcheck = QTimer(self) self._hangcheck = QTimer(self)
self.connect(self._hangcheck, SIGNAL('timeout()'), self.hangcheck) self.connect(self._hangcheck, SIGNAL('timeout()'), self.hangcheck)
self.cf_start_time = time.time() self.cf_start_time = time.time()
self.pi.start(_('Downloading cover...')) self.pi.start(_('Downloading cover...'))
self._hangcheck.start(100) self._hangcheck.start(100)
else:
error_dialog(self, _('Cannot fetch cover'),
_('You must specify the ISBN identifier for this book.')).exec_()
def hangcheck(self): def hangcheck(self):
if not (self.cover_fetcher.isFinished() or time.time()-self.cf_start_time > 150): if not self.cover_fetcher.isFinished() and \
time.time()-self.cf_start_time < self.COVER_FETCH_TIMEOUT:
return return
self._hangcheck.stop() self._hangcheck.stop()
@ -385,6 +413,11 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
_('<b>Could not fetch cover.</b><br/>')+ _('<b>Could not fetch cover.</b><br/>')+
_('The download timed out.')).exec_() _('The download timed out.')).exec_()
return return
if self.cover_fetcher.needs_isbn:
error_dialog(self, _('Cannot fetch cover'),
_('Could not find cover for this book. Try '
'specifying the ISBN first.')).exec_()
return
if self.cover_fetcher.exception is not None: if self.cover_fetcher.exception is not None:
err = self.cover_fetcher.exception err = self.cover_fetcher.exception
error_dialog(self, _('Cannot fetch cover'), error_dialog(self, _('Cannot fetch cover'),

View File

@ -1,7 +1,8 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class> <class>Dialog</class>
<widget class="QDialog" name="Dialog" > <widget class="QDialog" name="Dialog">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
@ -9,66 +10,70 @@
<height>209</height> <height>209</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>Password needed</string> <string>Password needed</string>
</property> </property>
<property name="windowIcon" > <property name="windowIcon">
<iconset resource="../images.qrc" >:/images/mimetypes/unknown.svg</iconset> <iconset resource="../images.qrc">
<normaloff>:/images/mimetypes/unknown.svg</normaloff>:/images/mimetypes/unknown.svg</iconset>
</property> </property>
<layout class="QGridLayout" > <layout class="QGridLayout">
<item row="0" column="1" > <item row="0" column="1">
<widget class="QLabel" name="msg" > <widget class="QLabel" name="msg">
<property name="text" > <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
<property name="openExternalLinks" > <property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" > <item row="1" column="0">
<widget class="QLabel" name="label" > <widget class="QLabel" name="label">
<property name="text" > <property name="text">
<string>&amp;Username:</string> <string>&amp;Username:</string>
</property> </property>
<property name="buddy" > <property name="buddy">
<cstring>gui_username</cstring> <cstring>gui_username</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1" > <item row="1" column="1">
<widget class="QLineEdit" name="gui_username" /> <widget class="QLineEdit" name="gui_username"/>
</item> </item>
<item row="2" column="0" > <item row="2" column="0">
<widget class="QLabel" name="label_2" > <widget class="QLabel" name="label_2">
<property name="text" > <property name="text">
<string>&amp;Password:</string> <string>&amp;Password:</string>
</property> </property>
<property name="buddy" > <property name="buddy">
<cstring>gui_password</cstring> <cstring>gui_password</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1" > <item row="2" column="1">
<widget class="QLineEdit" name="gui_password" > <widget class="QLineEdit" name="gui_password">
<property name="echoMode" > <property name="echoMode">
<enum>QLineEdit::Password</enum> <enum>QLineEdit::Password</enum>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1" > <item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox" > <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation" > <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="standardButtons" > <property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1" > <item row="3" column="1">
<widget class="QCheckBox" name="show_password" > <widget class="QCheckBox" name="show_password">
<property name="text" > <property name="text">
<string>&amp;Show password</string> <string>&amp;Show password</string>
</property> </property>
</widget> </widget>
@ -76,7 +81,7 @@
</layout> </layout>
</widget> </widget>
<resources> <resources>
<include location="../images.qrc" /> <include location="../images.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>
@ -85,11 +90,11 @@
<receiver>Dialog</receiver> <receiver>Dialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel">
<x>248</x> <x>248</x>
<y>254</y> <y>254</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel">
<x>157</x> <x>157</x>
<y>274</y> <y>274</y>
</hint> </hint>
@ -101,11 +106,11 @@
<receiver>Dialog</receiver> <receiver>Dialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel">
<x>316</x> <x>316</x>
<y>260</y> <y>260</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel">
<x>286</x> <x>286</x>
<y>274</y> <y>274</y>
</hint> </hint>