diff --git a/setup.py b/setup.py index 008f1c301f..82e8056c38 100644 --- a/setup.py +++ b/setup.py @@ -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' : [ diff --git a/src/libprs500/ebooks/metadata/library_thing.py b/src/libprs500/ebooks/metadata/library_thing.py new file mode 100644 index 0000000000..49363650b3 --- /dev/null +++ b/src/libprs500/ebooks/metadata/library_thing.py @@ -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()) \ No newline at end of file diff --git a/src/libprs500/gui2/dialogs/metadata_single.py b/src/libprs500/gui2/dialogs/metadata_single.py index 8451a36358..c7afcd00bc 100644 --- a/src/libprs500/gui2/dialogs/metadata_single.py +++ b/src/libprs500/gui2/dialogs/metadata_single.py @@ -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', + _('

Enter your username and password for LibraryThing.com.
If you do not have one, you can register for free!.

')) + 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'), _('Could not fetch cover.
')+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_()