mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
332dbf4444
commit
c2b79fe5d9
@ -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
|
||||||
|
@ -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()
|
||||||
|
@ -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 d.username() or not d.password():
|
if not gui_conf['asked_library_thing_password'] and \
|
||||||
|
(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'),
|
||||||
|
@ -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>&Username:</string>
|
<string>&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>&Password:</string>
|
<string>&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>&Show password</string>
|
<string>&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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user