2 # -*- coding: utf-8 -*-
4 # This file is part of Librarian, licensed under GNU Affero GPLv3 or later.
5 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
10 from librarian import DirDocProvider, ParseError
11 from librarian.parser import WLDocument
12 from librarian.cover import DefaultEbookCover
16 """Option for optparse. Use it like `optparse.OptionParser.add_option`."""
17 def __init__(self, *names, **options):
19 self.options = options
21 def add(self, parser):
22 parser.add_option(*self.names, **self.options)
25 return self.options['dest']
27 def value(self, options):
28 return getattr(options, self.name())
31 class Book2Anything(object):
32 """A class for creating book2... scripts.
34 Subclass it for any format you want to convert to.
36 format_name = None # Set format name, like "PDF".
37 ext = None # Set file extension, like "pdf".
38 uses_cover = False # Can it add a cover?
39 cover_optional = True # Only relevant if uses_cover
40 uses_provider = False # Does it need a DocProvider?
41 transform = None # Transform method. Uses WLDocument.as_{ext} by default.
42 parser_options = [] # List of Option objects for additional parser args.
43 transform_options = [] # List of Option objects for additional transform args.
44 transform_flags = [] # List of Option objects for supported transform flags.
48 # Parse commandline arguments
49 usage = """Usage: %%prog [options] SOURCE [SOURCE...]
50 Convert SOURCE files to %s format.""" % cls.format_name
52 parser = optparse.OptionParser(usage=usage)
54 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False,
55 help='print status messages to stdout')
56 parser.add_option('-d', '--make-dir', action='store_true', dest='make_dir', default=False,
57 help='create a directory for author and put the output file in it')
58 parser.add_option('-o', '--output-file', dest='output_file', metavar='FILE',
59 help='specifies the output file')
60 parser.add_option('-O', '--output-dir', dest='output_dir', metavar='DIR',
61 help='specifies the directory for output')
63 if cls.cover_optional:
64 parser.add_option('-c', '--with-cover', action='store_true', dest='with_cover', default=False,
65 help='create default cover')
66 parser.add_option('-C', '--image-cache', dest='image_cache', metavar='URL',
67 help='prefix for image download cache' +
68 (' (implies --with-cover)' if cls.cover_optional else ''))
69 for option in cls.parser_options + cls.transform_options + cls.transform_flags:
72 options, input_filenames = parser.parse_args()
74 if len(input_filenames) < 1:
78 # Prepare additional args for parser.
80 for option in cls.parser_options:
81 parser_args[option.name()] = option.value(options)
82 # Prepare additional args for transform method.
84 for option in cls.transform_options:
85 transform_args[option.name()] = option.value(options)
86 # Add flags to transform_args, if any.
87 transform_flags = [flag.name() for flag in cls.transform_flags if flag.value(options)]
89 transform_args['flags'] = transform_flags
91 transform_args['verbose'] = True
92 # Add cover support, if any.
94 if options.image_cache:
95 def cover_class(*args, **kwargs):
96 return DefaultEbookCover(image_cache=options.image_cache, *args, **kwargs)
97 transform_args['cover'] = cover_class
98 elif not cls.cover_optional or options.with_cover:
99 transform_args['cover'] = DefaultEbookCover
103 for main_input in input_filenames:
107 # Where to find input?
108 if cls.uses_provider:
109 path, fname = os.path.realpath(main_input).rsplit('/', 1)
110 provider = DirDocProvider(path)
114 # Where to write output?
115 if not (options.output_file or options.output_dir):
116 output_file = os.path.splitext(main_input)[0] + '.' + cls.ext
118 output_file = options.output_file
120 # Do the transformation.
121 doc = WLDocument.from_file(main_input, provider=provider, **parser_args)
122 transform = cls.transform
123 if transform is None:
124 transform = getattr(WLDocument, 'as_%s' % cls.ext)
125 output = transform(doc, **transform_args)
127 doc.save_output_file(output, output_file, options.output_dir, options.make_dir, cls.ext)
129 except ParseError, e:
130 print '%(file)s:%(name)s:%(message)s' % {
132 'name': e.__class__.__name__,