Nicebook update

This commit is contained in:
Sengian 2010-12-08 07:09:26 +01:00
commit 7e7eb2cad3
6 changed files with 91 additions and 25 deletions

View File

@ -4,6 +4,7 @@ __copyright__ = '2010, sengian <sengian1@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, textwrap, re, traceback, socket import sys, textwrap, re, traceback, socket
from Queue import Queue
from urllib import urlencode from urllib import urlencode
from lxml.html import soupparser, tostring from lxml.html import soupparser, tostring
@ -39,6 +40,8 @@ class Fictionwise(MetadataSource): # {{{
class FictionwiseError(Exception): class FictionwiseError(Exception):
pass pass
def report(verbose): def report(verbose):
if verbose: if verbose:
traceback.print_exc() traceback.print_exc()

View File

@ -3,7 +3,8 @@ __license__ = 'GPL 3'
__copyright__ = '2010, sengian <sengian1@gmail.com>' __copyright__ = '2010, sengian <sengian1@gmail.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import sys, textwrap, re, traceback, socket, threading import sys, textwrap, re, traceback, socket
from threading import Thread
from Queue import Queue from Queue import Queue
from urllib import urlencode from urllib import urlencode
from math import ceil from math import ceil
@ -79,16 +80,16 @@ class NiceBooksError(Exception):
class ISBNNotFound(NiceBooksError): class ISBNNotFound(NiceBooksError):
pass pass
class BrowserThread(threading.Thread): class BrowserThread(Thread):
def __init__(self, url, verbose=False, timeout=10., ex=Exception, name='Meta'): def __init__(self, url, verbose=False, timeout=10., ex=Exception, name='Meta'):
self.url = url self.url = url
self.ex = ex self.ex = ex
self.name = name self.plugname = name
self.verbose = verbose self.verbose = verbose
self.timeout = timeout self.timeout = timeout
self.result = None self.result = None
threading.Thread.__init__(self) Thread.__init__(self)
def get_result(self): def get_result(self):
return self.result return self.result
@ -102,8 +103,8 @@ class BrowserThread(threading.Thread):
e.getcode() == 404: e.getcode() == 404:
self.result = None self.result = None
if isinstance(getattr(e, 'args', [None])[0], socket.timeout): if isinstance(getattr(e, 'args', [None])[0], socket.timeout):
raise self.ex(_('%s timed out. Try again later.') % self.name) raise self.ex(_('%s timed out. Try again later.') % self.plugname)
raise self.ex(_('%s encountered an error.') % self.name) raise self.ex(_('%s encountered an error.') % self.plugname)
if '<title>404 - ' in raw: if '<title>404 - ' in raw:
report(self.verbose) report(self.verbose)
self.result = None self.result = None
@ -292,10 +293,11 @@ class ResultList(list):
while len(self) < total_entries: while len(self) < total_entries:
thread = q.get(True) thread = q.get(True)
thread.join() thread.join()
mi, order = thread.get_result() mi = thread.get_result()
if mi is None: if mi is None:
self.append(None) self.append(None)
self.append(self.fill_MI(mi, verbose)) else:
self.append(self.fill_MI(mi, verbose))
def populate(self, entries, verbose=False, brcall=3): def populate(self, entries, verbose=False, brcall=3):
if len(entries) == 1 and not isinstance(entries[0], str): if len(entries) == 1 and not isinstance(entries[0], str):
@ -306,8 +308,8 @@ class ResultList(list):
else: else:
#multiple entries #multiple entries
q = Queue(brcall) q = Queue(brcall)
prod_thread = threading.Thread(target=self.producer, args=(q, entries, verbose)) prod_thread = Thread(target=self.producer, args=(q, entries, verbose))
cons_thread = threading.Thread(target=self.consumer, args=(q, len(entries), verbose)) cons_thread = Thread(target=self.consumer, args=(q, len(entries), verbose))
prod_thread.start() prod_thread.start()
cons_thread.start() cons_thread.start()
prod_thread.join() prod_thread.join()
@ -362,7 +364,7 @@ def search(title=None, author=None, publisher=None, isbn=None,
#List of entry #List of entry
ans = ResultList() ans = ResultList()
ans.populate(entries, verbose) ans.populate(entries, verbose)
return ans return [x for x in ans if x]
def check_for_cover(isbn): def check_for_cover(isbn):
br = browser() br = browser()

View File

@ -5,13 +5,67 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QMenu from functools import partial
from PyQt4.Qt import QMenu, QObject, QTimer
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog
from calibre.gui2.dialogs.confirm_delete import confirm from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
single_shot = partial(QTimer.singleShot, 10)
class MultiDeleter(QObject):
def __init__(self, gui, rows, callback):
from calibre.gui2.dialogs.progress import ProgressDialog
QObject.__init__(self, gui)
self.model = gui.library_view.model()
self.ids = list(map(self.model.id, rows))
self.gui = gui
self.failures = []
self.deleted_ids = []
self.callback = callback
single_shot(self.delete_one)
self.pd = ProgressDialog(_('Deleting...'), parent=gui,
cancelable=False, min=0, max=len(self.ids))
self.pd.setModal(True)
self.pd.show()
def delete_one(self):
if not self.ids:
self.cleanup()
return
id_ = self.ids.pop()
title = 'id:%d'%id_
try:
title_ = self.model.db.title(id_, index_is_id=True)
if title_:
title = title_
self.model.db.delete_book(id_, notify=False, commit=False)
self.deleted_ids.append(id_)
except:
import traceback
self.failures.append((id_, title, traceback.format_exc()))
single_shot(self.delete_one)
self.pd.value += 1
self.pd.set_msg(_('Deleted') + ' ' + title)
def cleanup(self):
self.pd.hide()
self.pd = None
self.model.db.commit()
self.model.db.clean()
self.model.books_deleted()
self.gui.tags_view.recount()
self.callback(self.deleted_ids)
if self.failures:
msg = ['==> '+x[1]+'\n'+x[2] for x in self.failures]
error_dialog(self.gui, _('Failed to delete'),
_('Failed to delete some books, click the Show Details button'
' for details.'), det_msg='\n\n'.join(msg), show=True)
class DeleteAction(InterfaceAction): class DeleteAction(InterfaceAction):
name = 'Remove Books' name = 'Remove Books'
@ -179,8 +233,13 @@ class DeleteAction(InterfaceAction):
row = None row = None
if ci.isValid(): if ci.isValid():
row = ci.row() row = ci.row()
ids_deleted = view.model().delete_books(rows) if len(rows) < 5:
self.library_ids_deleted(ids_deleted, row) ids_deleted = view.model().delete_books(rows)
self.library_ids_deleted(ids_deleted, row)
else:
self.__md = MultiDeleter(self.gui, rows,
partial(self.library_ids_deleted, current_row=row))
else: else:
if not confirm('<p>'+_('The selected books will be ' if not confirm('<p>'+_('The selected books will be '
'<b>permanently deleted</b> ' '<b>permanently deleted</b> '

View File

@ -223,21 +223,22 @@ class BooksModel(QAbstractTableModel): # {{{
def by_author(self): def by_author(self):
return self.sorted_on[0] == 'authors' return self.sorted_on[0] == 'authors'
def books_deleted(self):
self.count_changed()
self.clear_caches()
self.reset()
def delete_books(self, indices): def delete_books(self, indices):
ids = map(self.id, indices) ids = map(self.id, indices)
for id in ids: for id in ids:
self.db.delete_book(id, notify=False) self.db.delete_book(id, notify=False)
self.count_changed() self.books_deleted()
self.clear_caches()
self.reset()
return ids return ids
def delete_books_by_id(self, ids): def delete_books_by_id(self, ids):
for id in ids: for id in ids:
self.db.delete_book(id) self.db.delete_book(id)
self.count_changed() self.books_deleted()
self.clear_caches()
self.reset()
def books_added(self, num): def books_added(self, num):
if num > 0: if num > 0:

View File

@ -953,23 +953,24 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.notify('metadata', [id]) self.notify('metadata', [id])
return True return True
def delete_book(self, id, notify=True): def delete_book(self, id, notify=True, commit=True):
''' '''
Removes book from the result cache and the underlying database. Removes book from the result cache and the underlying database.
If you set commit to False, you must call clean() manually afterwards
''' '''
try: try:
path = os.path.join(self.library_path, self.path(id, index_is_id=True)) path = os.path.join(self.library_path, self.path(id, index_is_id=True))
except: except:
path = None path = None
self.data.remove(id)
if path and os.path.exists(path): if path and os.path.exists(path):
self.rmtree(path) self.rmtree(path)
parent = os.path.dirname(path) parent = os.path.dirname(path)
if len(os.listdir(parent)) == 0: if len(os.listdir(parent)) == 0:
self.rmtree(parent) self.rmtree(parent)
self.conn.execute('DELETE FROM books WHERE id=?', (id,)) self.conn.execute('DELETE FROM books WHERE id=?', (id,))
self.conn.commit() if commit:
self.clean() self.conn.commit()
self.clean()
self.data.books_deleted([id]) self.data.books_deleted([id])
if notify: if notify:
self.notify('delete', [id]) self.notify('delete', [id])

View File

@ -166,7 +166,7 @@ class Feed(object):
self.articles.append(article) self.articles.append(article)
else: else:
t = strftime(u'%a, %d %b, %Y %H:%M', article.localtime.timetuple()) t = strftime(u'%a, %d %b, %Y %H:%M', article.localtime.timetuple())
self.logger.debug('Skipping article %s (%s) from feed %s as it is too old.'% self.logger.debug(u'Skipping article %s (%s) from feed %s as it is too old.'%
(title, t, self.title)) (title, t, self.title))
d = item.get('date', '') d = item.get('date', '')
article.formatted_date = d article.formatted_date = d