mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	Framework for plugins to generate 'catalogs' from the calibre database.
This commit is contained in:
		
						commit
						e56f6b08f4
					
				@ -48,7 +48,7 @@ class Plugin(object):
 | 
				
			|||||||
    #: the plugins are run in order of decreasing priority
 | 
					    #: the plugins are run in order of decreasing priority
 | 
				
			||||||
    #: i.e. plugins with higher priority will be run first.
 | 
					    #: i.e. plugins with higher priority will be run first.
 | 
				
			||||||
    #: The highest possible priority is ``sys.maxint``.
 | 
					    #: The highest possible priority is ``sys.maxint``.
 | 
				
			||||||
    #: Default pririty is 1.
 | 
					    #: Default priority is 1.
 | 
				
			||||||
    priority = 1
 | 
					    priority = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #: The earliest version of calibre this plugin requires
 | 
					    #: The earliest version of calibre this plugin requires
 | 
				
			||||||
@ -226,4 +226,75 @@ class MetadataWriterPlugin(Plugin):
 | 
				
			|||||||
        '''
 | 
					        '''
 | 
				
			||||||
        pass
 | 
					        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')
 | 
				
			||||||
 | 
				
			|||||||
@ -421,7 +421,8 @@ from calibre.devices.binatone.driver import README
 | 
				
			|||||||
from calibre.devices.hanvon.driver import N516
 | 
					from calibre.devices.hanvon.driver import N516
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre.ebooks.metadata.fetch import GoogleBooks, ISBNDB, Amazon
 | 
					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 += [
 | 
					plugins += [
 | 
				
			||||||
    ComicInput,
 | 
					    ComicInput,
 | 
				
			||||||
    EPUBInput,
 | 
					    EPUBInput,
 | 
				
			||||||
 | 
				
			|||||||
@ -5,8 +5,8 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
 | 
				
			|||||||
import os, shutil, traceback, functools, sys, re
 | 
					import os, shutil, traceback, functools, sys, re
 | 
				
			||||||
from contextlib import closing
 | 
					from contextlib import closing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre.customize import Plugin, FileTypePlugin, MetadataReaderPlugin, \
 | 
					from calibre.customize import Plugin, CatalogPlugin, FileTypePlugin, \
 | 
				
			||||||
                              MetadataWriterPlugin
 | 
					                              MetadataReaderPlugin, MetadataWriterPlugin
 | 
				
			||||||
from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
 | 
					from calibre.customize.conversion import InputFormatPlugin, OutputFormatPlugin
 | 
				
			||||||
from calibre.customize.profiles import InputProfile, OutputProfile
 | 
					from calibre.customize.profiles import InputProfile, OutputProfile
 | 
				
			||||||
from calibre.customize.builtins import plugins as builtin_plugins
 | 
					from calibre.customize.builtins import plugins as builtin_plugins
 | 
				
			||||||
@ -300,6 +300,7 @@ def find_plugin(name):
 | 
				
			|||||||
        if plugin.name == name:
 | 
					        if plugin.name == name:
 | 
				
			||||||
            return plugin
 | 
					            return plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def input_format_plugins():
 | 
					def input_format_plugins():
 | 
				
			||||||
    for plugin in _initialized_plugins:
 | 
					    for plugin in _initialized_plugins:
 | 
				
			||||||
        if isinstance(plugin, InputFormatPlugin):
 | 
					        if isinstance(plugin, InputFormatPlugin):
 | 
				
			||||||
@ -328,6 +329,7 @@ def available_input_formats():
 | 
				
			|||||||
    formats.add('zip'), formats.add('rar')
 | 
					    formats.add('zip'), formats.add('rar')
 | 
				
			||||||
    return formats
 | 
					    return formats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def output_format_plugins():
 | 
					def output_format_plugins():
 | 
				
			||||||
    for plugin in _initialized_plugins:
 | 
					    for plugin in _initialized_plugins:
 | 
				
			||||||
        if isinstance(plugin, OutputFormatPlugin):
 | 
					        if isinstance(plugin, OutputFormatPlugin):
 | 
				
			||||||
@ -347,6 +349,27 @@ def available_output_formats():
 | 
				
			|||||||
            formats.add(plugin.file_type)
 | 
					            formats.add(plugin.file_type)
 | 
				
			||||||
    return formats
 | 
					    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():
 | 
					def device_plugins():
 | 
				
			||||||
    for plugin in _initialized_plugins:
 | 
					    for plugin in _initialized_plugins:
 | 
				
			||||||
        if isinstance(plugin, DevicePlugin):
 | 
					        if isinstance(plugin, DevicePlugin):
 | 
				
			||||||
 | 
				
			|||||||
@ -583,8 +583,120 @@ def command_export(args, dbpath):
 | 
				
			|||||||
    do_export(get_db(dbpath, opts), ids, dir, opts)
 | 
					    do_export(get_db(dbpath, opts), ids, dir, opts)
 | 
				
			||||||
    return 0
 | 
					    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',
 | 
					COMMANDS = ('list', 'add', 'remove', 'add_format', 'remove_format',
 | 
				
			||||||
                'show_metadata', 'set_metadata', 'export')
 | 
					                'show_metadata', 'set_metadata', 'export', 'catalog')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def option_parser():
 | 
					def option_parser():
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user