Framework for plugins to generate 'catalogs' from the calibre database.

This commit is contained in:
Kovid Goyal 2010-01-13 11:25:43 -07:00
commit e56f6b08f4
4 changed files with 212 additions and 5 deletions

View File

@ -48,7 +48,7 @@ class Plugin(object):
#: the plugins are run in order of decreasing priority
#: i.e. plugins with higher priority will be run first.
#: The highest possible priority is ``sys.maxint``.
#: Default pririty is 1.
#: Default priority is 1.
priority = 1
#: The earliest version of calibre this plugin requires
@ -226,4 +226,75 @@ class MetadataWriterPlugin(Plugin):
'''
pass
class CatalogPlugin(Plugin):
'''
A plugin that implements a catalog generator.
'''
#: Output file type for which this plugin should be run
#: For example: 'epub' or 'xml'
file_types = set([])
type = _('Catalog generator')
#: CLI parser options specific to this plugin, declared as namedtuple Option
#:
#: from collections import namedtuple
#: Option = namedtuple('Option', 'option, default, dest, help')
#: cli_options = [Option('--catalog-title',
#: default = 'My Catalog',
#: dest = 'catalog_title',
#: help = (_('Title of generated catalog. \nDefault:') + " '" +
#: '%default' + "'"))]
cli_options = []
def search_sort_db_as_dict(self, db, opts):
if opts.search_text:
db.search(opts.search_text)
if opts.sort_by:
# 2nd arg = ascending
db.sort(opts.sort_by, True)
return db.get_data_as_dict()
def get_output_fields(self, opts):
# Return a list of requested fields, with opts.sort_by first
all_fields = set(
['author_sort','authors','comments','cover','formats', 'id','isbn','pubdate','publisher','rating',
'series_index','series','size','tags','timestamp',
'title','uuid'])
fields = all_fields
if opts.fields != 'all':
# Make a list from opts.fields
requested_fields = set(opts.fields.split(','))
fields = list(all_fields & requested_fields)
else:
fields = list(all_fields)
fields.sort()
fields.insert(0,fields.pop(int(fields.index(opts.sort_by))))
return fields
def run(self, path_to_output, opts, db):
'''
Run the plugin. Must be implemented in subclasses.
It should generate the catalog in the format specified
in file_types, returning the absolute path to the
generated catalog file. If an error is encountered
it should raise an Exception and return None. The default
implementation simply returns None.
The generated catalog file should be created with the
:meth:`temporary_file` method.
:param path_to_output: Absolute path to the generated catalog file.
:param opts: A dictionary of keyword arguments
:param db: A LibraryDatabase2 object
:return: None
'''
# Default implementation does nothing
raise NotImplementedError('CatalogPlugin.generate_catalog() default '
'method, should be overridden in subclass')

View File

@ -421,7 +421,8 @@ from calibre.devices.binatone.driver import README
from calibre.devices.hanvon.driver import N516
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon]
from calibre.library.catalog import CSV_XML
plugins = [HTML2ZIP, PML2PMLZ, GoogleBooks, ISBNDB, Amazon, CSV_XML]
plugins += [
ComicInput,
EPUBInput,

View File

@ -5,8 +5,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import os, shutil, traceback, functools, sys, re
from contextlib import closing
from calibre.customize import Plugin, FileTypePlugin, MetadataReaderPlugin, \
MetadataWriterPlugin
from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
MetadataReaderPlugin, MetadataWriterPlugin
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
from calibre.customize.profiles import InputProfile, OutputProfile
from calibre.customize.builtins import plugins as builtin_plugins
@ -300,6 +300,7 @@ def find_plugin(name):
if plugin.name == name:
return plugin
def input_format_plugins():
for plugin in _initialized_plugins:
if isinstance(plugin, InputFormatPlugin):
@ -328,6 +329,7 @@ def available_input_formats():
formats.add('zip'), formats.add('rar')
return formats
def output_format_plugins():
for plugin in _initialized_plugins:
if isinstance(plugin, OutputFormatPlugin):
@ -347,6 +349,27 @@ def available_output_formats():
formats.add(plugin.file_type)
return formats
def catalog_plugins():
for plugin in _initialized_plugins:
if isinstance(plugin, CatalogPlugin):
yield plugin
def available_catalog_formats():
formats = set([])
for plugin in catalog_plugins():
if not is_disabled(plugin):
for format in plugin.file_types:
formats.add(format)
return formats
def plugin_for_catalog_format(fmt):
for plugin in catalog_plugins():
if fmt.lower() in plugin.file_types:
return plugin
else:
return None
def device_plugins():
for plugin in _initialized_plugins:
if isinstance(plugin, DevicePlugin):

View File

@ -583,8 +583,120 @@ def command_export(args, dbpath):
do_export(get_db(dbpath, opts), ids, dir, opts)
return 0
# GR additions
def catalog_option_parser(args):
from calibre.customize.ui import available_catalog_formats, plugin_for_catalog_format
from calibre.utils.logging import Log
def add_plugin_parser_options(fmt, parser, log):
# Fetch the extension-specific CLI options from the plugin
plugin = plugin_for_catalog_format(fmt)
for option in plugin.cli_options:
parser.add_option(option.option,
default=option.default,
dest=option.dest,
help=option.help)
return plugin
def print_help(parser, log):
help = parser.format_help().encode(preferred_encoding, 'replace')
log(help)
def validate_command_line(parser, args, log):
# calibredb catalog path/to/destination.[epub|csv|xml|...] [options]
# Validate form
if not len(args) or args[0].startswith('-'):
print_help(parser, log)
log.error("\n\nYou must specify a catalog output file of the form 'path/to/destination.extension'\n"
"To review options for an output format, type 'calibredb catalog <.extension> --help'\n"
"For example, 'calibredb catalog .xml --help'\n")
raise SystemExit(1)
# Validate plugin exists for specified output format
output = os.path.abspath(args[0])
file_extension = output[output.rfind('.') + 1:]
if not file_extension in available_catalog_formats():
print_help(parser, log)
log.error("No catalog plugin available for extension '%s'.\n" % file_extension +
"Catalog plugins available for %s\n" % ', '.join(available_catalog_formats()) )
raise SystemExit(1)
return output, file_extension
# Entry point
log = Log()
parser = get_parser(_(
'''
%prog catalog /path/to/destination.(epub|csv|xml|...) [options]
Export a catalog in format specified by path/to/destination extension.
Options control how entries are displayed in the generated catalog ouput.
'''))
# Confirm that a plugin handler exists for specified output file extension
# Will raise SystemExit(1) if no plugin matching file_extension
output, fmt = validate_command_line(parser, args, log)
# Add options common to all catalog plugins
parser.add_option('-s', '--search', default=None, dest='search_text',
help=_("Filter the results by the search query. For the format of the search query, please see the search-related documentation in the User Manual.\n"+
"Default: no filtering"))
parser.add_option('-v','--verbose', default=False, action='store_true',
dest='verbose',
help=_('Show detailed output information. Useful for debugging'))
# Add options specific to fmt plugin
plugin = add_plugin_parser_options(fmt, parser, log)
# Merge options from GUI Preferences
'''
from calibre.library.save_to_disk import config
c = config()
for pref in ['asciiize', 'update_metadata', 'write_opf', 'save_cover']:
opt = c.get_option(pref)
switch = '--dont-'+pref.replace('_', '-')
parser.add_option(switch, default=True, action='store_false',
help=opt.help+' '+_('Specifying this switch will turn '
'this behavior off.'), dest=pref)
for pref in ['timefmt', 'template', 'formats']:
opt = c.get_option(pref)
switch = '--'+pref
parser.add_option(switch, default=opt.default,
help=opt.help, dest=pref)
for pref in ('replace_whitespace', 'to_lowercase'):
opt = c.get_option(pref)
switch = '--'+pref.replace('_', '-')
parser.add_option(switch, default=False, action='store_true',
help=opt.help)
'''
return parser, plugin, log
def command_catalog(args, dbpath):
parser, plugin, log = catalog_option_parser(args)
opts, args = parser.parse_args(sys.argv[1:])
if len(args) < 2:
parser.print_help()
print
print >>sys.stderr, _('Error: You must specify a catalog output file')
return 1
if opts.verbose:
log("library.cli:command_catalog dispatching to plugin %s" % plugin.name)
plugin.run(args[1], opts, get_db(dbpath, opts))
return 0
# end of GR additions
COMMANDS = ('list', 'add', 'remove', 'add_format', 'remove_format',
'show_metadata', 'set_metadata', 'export')
'show_metadata', 'set_metadata', 'export', 'catalog')
def option_parser():