mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Nicebook update
This commit is contained in:
commit
7e7eb2cad3
@ -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()
|
||||||
|
@ -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()
|
||||||
|
@ -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> '
|
||||||
|
@ -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:
|
||||||
|
@ -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])
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user