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',
|
||||
'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' : [
|
||||
|
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
|
||||
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_()
|
||||
|
Loading…
x
Reference in New Issue
Block a user