Add a callback to FileTypePlugins that is useful for modifying the book record in the database when it is first created

This commit is contained in:
Kovid Goyal 2016-01-04 18:58:50 +05:30
parent 8fce6f04ef
commit c956a64aa3
4 changed files with 57 additions and 29 deletions

View File

@ -179,8 +179,7 @@ class Plugin(object): # {{{
help_text = self.customization_help(gui=True) help_text = self.customization_help(gui=True)
help_text = QLabel(help_text, config_dialog) help_text = QLabel(help_text, config_dialog)
help_text.setWordWrap(True) help_text.setWordWrap(True)
help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse help_text.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
| Qt.LinksAccessibleByKeyboard)
help_text.setOpenExternalLinks(True) help_text.setOpenExternalLinks(True)
v.addWidget(help_text) v.addWidget(help_text)
sc = plugin_customization(self) sc = plugin_customization(self)
@ -369,6 +368,21 @@ class FileTypePlugin(Plugin): # {{{
''' '''
pass # Default implementation does nothing pass # Default implementation does nothing
def postadd(self, book_id, fmt_map, db):
'''
Called post add, i.e. after a book has been added to the db. Note that
this is different from :meth:`postimport`, which is called after a single book file
has been added to a book. postadd() is called only when an entire book record
with possibly more than one book file has been created for the first time.
This is useful if you wish to modify the book record in the database when the
book is first added to calibre.
:param book_id: Database id of the added book.
:param fmt_map: Map of file format to path from which the file format was added
:param db: Library database
'''
pass # Default implementation does nothing
# }}} # }}}
class MetadataReaderPlugin(Plugin): # {{{ class MetadataReaderPlugin(Plugin): # {{{

View File

@ -3,6 +3,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, shutil, traceback, functools, sys import os, shutil, traceback, functools, sys
from collections import defaultdict
from calibre.customize import (CatalogPlugin, FileTypePlugin, PluginNotFound, from calibre.customize import (CatalogPlugin, FileTypePlugin, PluginNotFound,
MetadataReaderPlugin, MetadataWriterPlugin, MetadataReaderPlugin, MetadataWriterPlugin,
@ -44,7 +45,7 @@ def find_plugin(name):
return plugin return plugin
def load_plugin(path_to_zip_file): # {{{ def load_plugin(path_to_zip_file): # {{{
''' '''
Load plugin from zip file or raise InvalidPlugin error Load plugin from zip file or raise InvalidPlugin error
@ -95,7 +96,8 @@ default_disabled_plugins = set([
]) ])
def is_disabled(plugin): def is_disabled(plugin):
if plugin.name in config['enabled_plugins']: return False if plugin.name in config['enabled_plugins']:
return False
return plugin.name in config['disabled_plugins'] or \ return plugin.name in config['disabled_plugins'] or \
plugin.name in default_disabled_plugins plugin.name in default_disabled_plugins
# }}} # }}}
@ -106,35 +108,30 @@ _on_import = {}
_on_postimport = {} _on_postimport = {}
_on_preprocess = {} _on_preprocess = {}
_on_postprocess = {} _on_postprocess = {}
_on_postadd = []
def reread_filetype_plugins(): def reread_filetype_plugins():
global _on_import global _on_import
global _on_postimport global _on_postimport
global _on_preprocess global _on_preprocess
global _on_postprocess global _on_postprocess
_on_import = {} _on_import = defaultdict(list)
_on_postimport = {} _on_postimport = defaultdict(list)
_on_preprocess = {} _on_preprocess = defaultdict(list)
_on_postprocess = {} _on_postprocess = defaultdict(list)
_on_postadd = []
for plugin in _initialized_plugins: for plugin in _initialized_plugins:
if isinstance(plugin, FileTypePlugin): if isinstance(plugin, FileTypePlugin):
for ft in plugin.file_types: for ft in plugin.file_types:
if plugin.on_import: if plugin.on_import:
if not _on_import.has_key(ft):
_on_import[ft] = []
_on_import[ft].append(plugin) _on_import[ft].append(plugin)
if plugin.on_postimport: if plugin.on_postimport:
if not _on_postimport.has_key(ft):
_on_postimport[ft] = []
_on_postimport[ft].append(plugin) _on_postimport[ft].append(plugin)
_on_postadd.append(plugin)
if plugin.on_preprocess: if plugin.on_preprocess:
if not _on_preprocess.has_key(ft):
_on_preprocess[ft] = []
_on_preprocess[ft].append(plugin) _on_preprocess[ft].append(plugin)
if plugin.on_postprocess: if plugin.on_postprocess:
if not _on_postprocess.has_key(ft):
_on_postprocess[ft] = []
_on_postprocess[ft].append(plugin) _on_postprocess[ft].append(plugin)
@ -187,6 +184,20 @@ def run_plugins_on_postimport(db, book_id, fmt):
plugin.name) plugin.name)
traceback.print_exc() traceback.print_exc()
def run_plugins_on_postadd(db, book_id, fmt_map):
customization = config['plugin_customization']
for plugin in _on_postadd:
if is_disabled(plugin):
continue
plugin.site_customization = customization.get(plugin.name, '')
with plugin:
try:
plugin.postadd(book_id, fmt_map, db)
except Exception:
print ('Running file type plugin %s failed with traceback:'%
plugin.name)
traceback.print_exc()
# }}} # }}}
# Plugin customization {{{ # Plugin customization {{{
@ -268,17 +279,14 @@ _metadata_writers = {}
def reread_metadata_plugins(): def reread_metadata_plugins():
global _metadata_readers global _metadata_readers
global _metadata_writers global _metadata_writers
_metadata_readers = {} _metadata_readers = defaultdict(list)
_metadata_writers = defaultdict(list)
for plugin in _initialized_plugins: for plugin in _initialized_plugins:
if isinstance(plugin, MetadataReaderPlugin): if isinstance(plugin, MetadataReaderPlugin):
for ft in plugin.file_types: for ft in plugin.file_types:
if not _metadata_readers.has_key(ft):
_metadata_readers[ft] = []
_metadata_readers[ft].append(plugin) _metadata_readers[ft].append(plugin)
elif isinstance(plugin, MetadataWriterPlugin): elif isinstance(plugin, MetadataWriterPlugin):
for ft in plugin.file_types: for ft in plugin.file_types:
if not _metadata_writers.has_key(ft):
_metadata_writers[ft] = []
_metadata_writers[ft].append(plugin) _metadata_writers[ft].append(plugin)
def metadata_readers(): def metadata_readers():
@ -338,7 +346,7 @@ def get_file_type_metadata(stream, ftype):
mi = MetaInformation(None, None) mi = MetaInformation(None, None)
ftype = ftype.lower().strip() ftype = ftype.lower().strip()
if _metadata_readers.has_key(ftype): if ftype in _metadata_readers:
for plugin in _metadata_readers[ftype]: for plugin in _metadata_readers[ftype]:
if not is_disabled(plugin): if not is_disabled(plugin):
with plugin: with plugin:
@ -355,7 +363,7 @@ def get_file_type_metadata(stream, ftype):
def set_file_type_metadata(stream, mi, ftype, report_error=None): def set_file_type_metadata(stream, mi, ftype, report_error=None):
ftype = ftype.lower().strip() ftype = ftype.lower().strip()
if _metadata_writers.has_key(ftype): if ftype in _metadata_writers:
for plugin in _metadata_writers[ftype]: for plugin in _metadata_writers[ftype]:
if not is_disabled(plugin): if not is_disabled(plugin):
with plugin: with plugin:
@ -708,4 +716,3 @@ def main(args=sys.argv):
if __name__ == '__main__': if __name__ == '__main__':
sys.exit(main()) sys.exit(main())
# }}} # }}}

View File

@ -15,7 +15,7 @@ from future_builtins import zip
from calibre import isbytestring, as_unicode from calibre import isbytestring, as_unicode
from calibre.constants import iswindows, preferred_encoding from calibre.constants import iswindows, preferred_encoding
from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimport from calibre.customize.ui import run_plugins_on_import, run_plugins_on_postimport, run_plugins_on_postadd
from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list from calibre.db import SPOOL_SIZE, _get_next_series_num_for_list
from calibre.db.categories import get_categories from calibre.db.categories import get_categories
from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock from calibre.db.locking import create_locks, DowngradeLockError, SafeReadLock
@ -1544,6 +1544,7 @@ class Cache(object):
as per the simple duplicate detection heuristic used by :meth:`has_book`. as per the simple duplicate detection heuristic used by :meth:`has_book`.
''' '''
duplicates, ids = [], [] duplicates, ids = [], []
fmt_map = {}
for mi, format_map in books: for mi, format_map in books:
book_id = self.create_book_entry(mi, add_duplicates=add_duplicates, apply_import_tags=apply_import_tags, preserve_uuid=preserve_uuid) book_id = self.create_book_entry(mi, add_duplicates=add_duplicates, apply_import_tags=apply_import_tags, preserve_uuid=preserve_uuid)
if book_id is None: if book_id is None:
@ -1551,7 +1552,9 @@ class Cache(object):
else: else:
ids.append(book_id) ids.append(book_id)
for fmt, stream_or_path in format_map.iteritems(): for fmt, stream_or_path in format_map.iteritems():
self.add_format(book_id, fmt, stream_or_path, dbapi=dbapi, run_hooks=run_hooks) if self.add_format(book_id, fmt, stream_or_path, dbapi=dbapi, run_hooks=run_hooks):
fmt_map[fmt.lower()] = getattr(stream_or_path, 'name', stream_or_path) or '<stream>'
run_plugins_on_postadd(dbapi or self, book_id, fmt_map)
return ids, duplicates return ids, duplicates
@write_api @write_api

View File

@ -16,7 +16,7 @@ from PyQt5.Qt import QObject, Qt, pyqtSignal
from calibre import prints, as_unicode from calibre import prints, as_unicode
from calibre.constants import DEBUG from calibre.constants import DEBUG
from calibre.customize.ui import run_plugins_on_postimport from calibre.customize.ui import run_plugins_on_postimport, run_plugins_on_postadd
from calibre.db.adding import find_books_in_directory from calibre.db.adding import find_books_in_directory
from calibre.db.utils import find_identical_books from calibre.db.utils import find_identical_books
from calibre.ebooks.metadata.book.base import Metadata from calibre.ebooks.metadata.book.base import Metadata
@ -375,7 +375,7 @@ class Adder(QObject):
[a('\t' + f) for f in paths] [a('\t' + f) for f in paths]
a(_('With error:')), a(traceback.format_exc()) a(_('With error:')), a(traceback.format_exc())
return return
self.add_formats(book_id, paths, mi) self.add_formats(book_id, paths, mi, is_an_add=True)
try: try:
if self.add_formats_to_existing: if self.add_formats_to_existing:
self.db.update_data_for_find_identical_books(book_id, self.find_identical_books_data) self.db.update_data_for_find_identical_books(book_id, self.find_identical_books_data)
@ -388,8 +388,9 @@ class Adder(QObject):
if DEBUG: if DEBUG:
prints('Added', mi.title, 'to db in: %.1f' % (time.time() - st)) prints('Added', mi.title, 'to db in: %.1f' % (time.time() - st))
def add_formats(self, book_id, paths, mi, replace=True): def add_formats(self, book_id, paths, mi, replace=True, is_an_add=False):
fmap = {p.rpartition(os.path.extsep)[-1].lower():p for p in paths} fmap = {p.rpartition(os.path.extsep)[-1].lower():p for p in paths}
fmt_map = {}
for fmt, path in fmap.iteritems(): for fmt, path in fmap.iteritems():
# The onimport plugins have already been run by the read metadata # The onimport plugins have already been run by the read metadata
# worker # worker
@ -398,11 +399,14 @@ class Adder(QObject):
try: try:
if self.db.add_format(book_id, fmt, path, run_hooks=False, replace=replace): if self.db.add_format(book_id, fmt, path, run_hooks=False, replace=replace):
run_plugins_on_postimport(self.dbref(), book_id, fmt) run_plugins_on_postimport(self.dbref(), book_id, fmt)
fmt_map[fmt.lower()] = path
except Exception: except Exception:
a = self.report.append a = self.report.append
a(''), a('-' * 70) a(''), a('-' * 70)
a(_('Failed to add the file {0} to the book: {1}').format(path, mi.title)) a(_('Failed to add the file {0} to the book: {1}').format(path, mi.title))
a(_('With error:')), a(traceback.format_exc()) a(_('With error:')), a(traceback.format_exc())
if is_an_add:
run_plugins_on_postadd(self.dbref(), book_id, fmt_map)
def process_duplicates(self): def process_duplicates(self):
if self.duplicates: if self.duplicates: