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',
'lrf2lrs = libprs500.ebooks.lrf.parser:main',
'isbndb = libprs500.ebooks.metadata.isbndb:main',
'librarything = libprs500.ebooks.metadata.library_thing:main',
'lrf2html = libprs500.ebooks.lrf.html.convert_to:main',
],
'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
add/remove formats
'''
import os, urllib, socket
import os
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
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.fetch_metadata import FetchMetadata
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.metadata.library_thing import login, cover_from_isbn, LibraryThingError
class Format(QListWidgetItem):
def __init__(self, parent, ext, path=None):
@ -215,38 +216,32 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
def fetch_cover(self):
isbn = qstring_to_unicode(self.isbn.text())
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.setCursor(Qt.WaitCursor)
QCoreApplication.instance().processEvents()
timeout = socket.getdefaulttimeout()
socket.setdefaulttimeout(5.)
try:
src = urllib.urlopen('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 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()
login(d.username(), d.password(), force=False)
cover_data = cover_from_isbn(isbn)[0]
pix = QPixmap()
pix.loadFromData(cover)
pix.loadFromData(cover_data)
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:
self.cover.setPixmap(pix)
self.cover_changed = True
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_()
finally:
self.fetch_cover_button.setEnabled(True)
self.unsetCursor()
socket.setdefaulttimeout(timeout)
else:
error_dialog(self, _('Cannot fetch cover'), _('You must specify the ISBN identifier for this book.')).exec_()