mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Making fetching of covers more robust and also create command line interface for it.
This commit is contained in:
parent
be6ee664c3
commit
532476d249
1
setup.py
1
setup.py
@ -38,6 +38,7 @@ entry_points = {
|
|||||||
'any2lrf = libprs500.ebooks.lrf.any.convert_from:main',
|
'any2lrf = libprs500.ebooks.lrf.any.convert_from:main',
|
||||||
'lrf2lrs = libprs500.ebooks.lrf.parser:main',
|
'lrf2lrs = libprs500.ebooks.lrf.parser:main',
|
||||||
'isbndb = libprs500.ebooks.metadata.isbndb:main',
|
'isbndb = libprs500.ebooks.metadata.isbndb:main',
|
||||||
|
'librarything = libprs500.ebooks.metadata.library_thing:main',
|
||||||
'lrf2html = libprs500.ebooks.lrf.html.convert_to:main',
|
'lrf2html = libprs500.ebooks.lrf.html.convert_to:main',
|
||||||
],
|
],
|
||||||
'gui_scripts' : [
|
'gui_scripts' : [
|
||||||
|
104
src/libprs500/ebooks/metadata/library_thing.py
Normal file
104
src/libprs500/ebooks/metadata/library_thing.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
## Copyright (C) 2008 Kovid Goyal kovid@kovidgoyal.net
|
||||||
|
## This program is free software; you can redistribute it and/or modify
|
||||||
|
## it under the terms of the GNU General Public License as published by
|
||||||
|
## the Free Software Foundation; either version 2 of the License, or
|
||||||
|
## (at your option) any later version.
|
||||||
|
##
|
||||||
|
## This program is distributed in the hope that it will be useful,
|
||||||
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
## GNU General Public License for more details.
|
||||||
|
##
|
||||||
|
## You should have received a copy of the GNU General Public License along
|
||||||
|
## with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
'''
|
||||||
|
Fetch cover from LibraryThing.com based on ISBN number.
|
||||||
|
'''
|
||||||
|
|
||||||
|
import sys, socket, os
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
from libprs500 import browser as _browser, __author__, __appname__, __version__
|
||||||
|
from libprs500.ebooks.BeautifulSoup import BeautifulSoup
|
||||||
|
browser = None
|
||||||
|
|
||||||
|
class LibraryThingError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ISBNNotFound(LibraryThingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ServerBusy(LibraryThingError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def login(username, password, force=True):
|
||||||
|
global browser
|
||||||
|
if browser is not None and not force:
|
||||||
|
return
|
||||||
|
browser = _browser()
|
||||||
|
browser.open('http://www.librarything.com')
|
||||||
|
browser.select_form('signup')
|
||||||
|
browser['formusername'] = username
|
||||||
|
browser['formpassword'] = password
|
||||||
|
browser.submit()
|
||||||
|
|
||||||
|
|
||||||
|
def cover_from_isbn(isbn, timeout=5.):
|
||||||
|
global browser
|
||||||
|
if browser is None:
|
||||||
|
browser = _browser()
|
||||||
|
_timeout = socket.getdefaulttimeout()
|
||||||
|
socket.setdefaulttimeout(timeout)
|
||||||
|
try:
|
||||||
|
src = browser.open('http://www.librarything.com/isbn/'+isbn).read()
|
||||||
|
s = BeautifulSoup(src)
|
||||||
|
url = s.find('td', attrs={'class':'left'})
|
||||||
|
if url is None:
|
||||||
|
if s.find('div', attrs={'class':'highloadwarning'}) is not None:
|
||||||
|
raise ServerBusy(_('Could not fetch cover as server is experiencing high load. Please try again later.'))
|
||||||
|
raise ISBNNotFound('ISBN: '+isbn+_(' not found.'))
|
||||||
|
url = url.find('img')
|
||||||
|
if url is None:
|
||||||
|
raise LibraryThingError(_('Server error. Try again later.'))
|
||||||
|
url = url['src']
|
||||||
|
cover_data = browser.open(url).read()
|
||||||
|
return cover_data, url.rpartition('.')[-1]
|
||||||
|
finally:
|
||||||
|
socket.setdefaulttimeout(_timeout)
|
||||||
|
|
||||||
|
def option_parser():
|
||||||
|
parser = OptionParser(epilog='Created by '+__author__,
|
||||||
|
version=__appname__+' '+__version__,
|
||||||
|
usage=\
|
||||||
|
'''
|
||||||
|
%prog [options] ISBN
|
||||||
|
|
||||||
|
Fetch a cover image for the book identified by ISBN from LibraryThing.com
|
||||||
|
''')
|
||||||
|
parser.add_option('-u', '--username', default=None,
|
||||||
|
help='Username for LibraryThing.com')
|
||||||
|
parser.add_option('-p', '--password', default=None,
|
||||||
|
help='Password for LibraryThing.com')
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def main(args=sys.argv):
|
||||||
|
parser = option_parser()
|
||||||
|
opts, args = parser.parse_args(args)
|
||||||
|
if len(args) != 2:
|
||||||
|
parser.print_help()
|
||||||
|
return 1
|
||||||
|
isbn = args[1]
|
||||||
|
if opts.username and opts.password:
|
||||||
|
login(opts.username, opts.password)
|
||||||
|
|
||||||
|
cover_data, ext = cover_from_isbn(isbn)
|
||||||
|
if not ext:
|
||||||
|
ext = 'jpg'
|
||||||
|
oname = os.path.abspath(isbn+'.'+ext)
|
||||||
|
open(oname, 'w').write(cover_data)
|
||||||
|
print 'Cover saved to file', oname
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
@ -16,7 +16,7 @@
|
|||||||
The dialog used to edit meta information for a book as well as
|
The dialog used to edit meta information for a book as well as
|
||||||
add/remove formats
|
add/remove formats
|
||||||
'''
|
'''
|
||||||
import os, urllib, socket
|
import os
|
||||||
|
|
||||||
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
|
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
|
||||||
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog
|
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog
|
||||||
@ -27,8 +27,9 @@ from libprs500.gui2 import qstring_to_unicode, error_dialog, file_icon_provider,
|
|||||||
from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
|
from libprs500.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog
|
||||||
from libprs500.gui2.dialogs.fetch_metadata import FetchMetadata
|
from libprs500.gui2.dialogs.fetch_metadata import FetchMetadata
|
||||||
from libprs500.gui2.dialogs.tag_editor import TagEditor
|
from libprs500.gui2.dialogs.tag_editor import TagEditor
|
||||||
from libprs500.ebooks.BeautifulSoup import BeautifulSoup
|
from libprs500.gui2.dialogs.password import PasswordDialog
|
||||||
from libprs500.ebooks import BOOK_EXTENSIONS
|
from libprs500.ebooks import BOOK_EXTENSIONS
|
||||||
|
from libprs500.ebooks.metadata.library_thing import login, cover_from_isbn, LibraryThingError
|
||||||
|
|
||||||
class Format(QListWidgetItem):
|
class Format(QListWidgetItem):
|
||||||
def __init__(self, parent, ext, path=None):
|
def __init__(self, parent, ext, path=None):
|
||||||
@ -215,38 +216,32 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
|
|||||||
def fetch_cover(self):
|
def fetch_cover(self):
|
||||||
isbn = qstring_to_unicode(self.isbn.text())
|
isbn = qstring_to_unicode(self.isbn.text())
|
||||||
if isbn:
|
if isbn:
|
||||||
|
d = 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>'))
|
||||||
|
if not d.username() or not d.password():
|
||||||
|
d.exec_()
|
||||||
|
if d.result() != PasswordDialog.Accepted:
|
||||||
|
return
|
||||||
self.fetch_cover_button.setEnabled(False)
|
self.fetch_cover_button.setEnabled(False)
|
||||||
self.setCursor(Qt.WaitCursor)
|
self.setCursor(Qt.WaitCursor)
|
||||||
QCoreApplication.instance().processEvents()
|
QCoreApplication.instance().processEvents()
|
||||||
timeout = socket.getdefaulttimeout()
|
|
||||||
socket.setdefaulttimeout(5.)
|
|
||||||
try:
|
try:
|
||||||
src = urllib.urlopen('http://www.librarything.com/isbn/'+isbn).read()
|
login(d.username(), d.password(), force=False)
|
||||||
s = BeautifulSoup(src)
|
cover_data = cover_from_isbn(isbn)[0]
|
||||||
url = s.find('td', attrs={'class':'left'})
|
|
||||||
if url is None:
|
|
||||||
if s.find('div', attrs={'class':'highloadwarning'}) is not None:
|
|
||||||
raise Exception('Could not fetch cover as server is experiencing high load. Please try again later.')
|
|
||||||
raise Exception('ISBN: '+isbn+' not found.')
|
|
||||||
url = url.find('img')
|
|
||||||
if url is None:
|
|
||||||
raise Exception('Server error. Try again later.')
|
|
||||||
url = url['src']
|
|
||||||
cover = urllib.urlopen(url).read()
|
|
||||||
pix = QPixmap()
|
pix = QPixmap()
|
||||||
pix.loadFromData(cover)
|
pix.loadFromData(cover_data)
|
||||||
if pix.isNull():
|
if pix.isNull():
|
||||||
error_dialog(self.window, url + " is not a valid picture").exec_()
|
error_dialog(self.window, "The cover is not a valid picture").exec_()
|
||||||
else:
|
else:
|
||||||
self.cover.setPixmap(pix)
|
self.cover.setPixmap(pix)
|
||||||
self.cover_changed = True
|
self.cover_changed = True
|
||||||
self.cpixmap = pix
|
self.cpixmap = pix
|
||||||
except Exception, err:
|
except LibraryThingError, err:
|
||||||
error_dialog(self, _('Could not fetch cover'), _('<b>Could not fetch cover.</b><br/>')+str(err)).exec_()
|
error_dialog(self, _('Could not fetch cover'), _('<b>Could not fetch cover.</b><br/>')+str(err)).exec_()
|
||||||
finally:
|
finally:
|
||||||
self.fetch_cover_button.setEnabled(True)
|
self.fetch_cover_button.setEnabled(True)
|
||||||
self.unsetCursor()
|
self.unsetCursor()
|
||||||
socket.setdefaulttimeout(timeout)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
error_dialog(self, _('Cannot fetch cover'), _('You must specify the ISBN identifier for this book.')).exec_()
|
error_dialog(self, _('Cannot fetch cover'), _('You must specify the ISBN identifier for this book.')).exec_()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user