Making fetching of covers more robust and also create command line interface for it.

This commit is contained in:
Kovid Goyal 2008-01-10 04:16:37 +00:00
parent be6ee664c3
commit 532476d249
3 changed files with 120 additions and 20 deletions

View File

@ -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' : [

View 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())

View File

@ -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_()