%PDF- %PDF-
Direktori : /lib/calibre/calibre/ebooks/metadata/ |
Current File : //lib/calibre/calibre/ebooks/metadata/cli.py |
__license__ = 'GPL v3' __copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' ''' ebook-meta ''' import sys, os, unicodedata from calibre.utils.config import StringConfig from calibre.customize.ui import metadata_readers, metadata_writers, force_identifiers from calibre.ebooks.metadata.meta import get_metadata, set_metadata from calibre.ebooks.metadata import string_to_authors, authors_to_sort_string, \ title_sort, MetaInformation from calibre.ebooks.lrf.meta import LRFMetaFile from calibre import prints from calibre.utils.date import parse_date from polyglot.builtins import iteritems USAGE=_('%prog ebook_file [options]\n') + \ _(''' Read/Write metadata from/to e-book files. Supported formats for reading metadata: {0} Supported formats for writing metadata: {1} Different file types support different kinds of metadata. If you try to set some metadata on a file type that does not support it, the metadata will be silently ignored. ''') def config(): c = StringConfig('') c.add_opt('title', ['-t', '--title'], help=_('Set the title.')) c.add_opt('authors', ['-a', '--authors'], help=_('Set the authors. Multiple authors should be separated ' 'by the & character. Author names should be in the order ' 'Firstname Lastname.')) c.add_opt('title_sort', ['--title-sort'], help=_('The version of the title to be used for sorting. ' 'If unspecified, and the title is specified, it will ' 'be auto-generated from the title.')) c.add_opt('author_sort', ['--author-sort'], help=_('String to be used when sorting by author. ' 'If unspecified, and the author(s) are specified, it will ' 'be auto-generated from the author(s).')) c.add_opt('cover', ['--cover'], help=_('Set the cover to the specified file.')) c.add_opt('comments', ['-c', '--comments'], help=_('Set the e-book description.')) c.add_opt('publisher', ['-p', '--publisher'], help=_('Set the e-book publisher.')) c.add_opt('category', ['--category'], help=_('Set the book category.')) c.add_opt('series', ['-s', '--series'], help=_('Set the series this e-book belongs to.')) c.add_opt('series_index', ['-i', '--index'], help=_('Set the index of the book in this series.')) c.add_opt('rating', ['-r', '--rating'], help=_('Set the rating. Should be a number between 1 and 5.')) c.add_opt('isbn', ['--isbn'], help=_('Set the ISBN of the book.')) c.add_opt('identifiers', ['--identifier'], action='append', help=_('Set the identifiers for the book, can be specified multiple times.' ' For example: --identifier uri:https://acme.com --identifier isbn:12345' ' To remove an identifier, specify no value, --identifier isbn:' ' Note that for EPUB files, an identifier marked as the package identifier cannot be removed.')) c.add_opt('tags', ['--tags'], help=_('Set the tags for the book. Should be a comma separated list.')) c.add_opt('book_producer', ['-k', '--book-producer'], help=_('Set the book producer.')) c.add_opt('language', ['-l', '--language'], help=_('Set the language.')) c.add_opt('pubdate', ['-d', '--date'], help=_('Set the published date.')) c.add_opt('get_cover', ['--get-cover'], help=_('Get the cover from the e-book and save it at as the ' 'specified file.')) c.add_opt('to_opf', ['--to-opf'], help=_('Specify the name of an OPF file. The metadata will ' 'be written to the OPF file.')) c.add_opt('from_opf', ['--from-opf'], help=_('Read metadata from the specified OPF file and use it to ' 'set metadata in the e-book. Metadata specified on the ' 'command line will override metadata read from the OPF file')) c.add_opt('lrf_bookid', ['--lrf-bookid'], help=_('Set the BookID in LRF files')) return c def filetypes(): readers = set() for r in metadata_readers(): readers = readers.union(set(r.file_types)) return readers def option_parser(): writers = set() for w in metadata_writers(): writers = writers.union(set(w.file_types)) ft, w = ', '.join(sorted(filetypes())), ', '.join(sorted(writers)) return config().option_parser(USAGE.format(ft, w)) def normalize(x): return unicodedata.normalize('NFC', x) def do_set_metadata(opts, mi, stream, stream_type): mi = MetaInformation(mi) for x in ('guide', 'toc', 'manifest', 'spine'): setattr(mi, x, None) from_opf = getattr(opts, 'from_opf', None) if from_opf is not None: from calibre.ebooks.metadata.opf2 import OPF opf_mi = OPF(open(from_opf, 'rb')).to_book_metadata() mi.smart_update(opf_mi) for pref in config().option_set.preferences: if pref.name in ('to_opf', 'from_opf', 'authors', 'title_sort', 'author_sort', 'get_cover', 'cover', 'tags', 'lrf_bookid', 'identifiers'): continue val = getattr(opts, pref.name, None) if val is not None: setattr(mi, pref.name, val) if getattr(opts, 'authors', None) is not None: mi.authors = string_to_authors(opts.authors) mi.author_sort = authors_to_sort_string(mi.authors) if getattr(opts, 'author_sort', None) is not None: mi.author_sort = opts.author_sort if getattr(opts, 'title_sort', None) is not None: mi.title_sort = opts.title_sort elif getattr(opts, 'title', None) is not None: mi.title_sort = title_sort(opts.title) if getattr(opts, 'tags', None) is not None: mi.tags = [t.strip() for t in opts.tags.split(',')] if getattr(opts, 'series', None) is not None: mi.series = opts.series.strip() if getattr(opts, 'series_index', None) is not None: mi.series_index = float(opts.series_index.strip()) if getattr(opts, 'pubdate', None) is not None: mi.pubdate = parse_date(opts.pubdate, assume_utc=False, as_utc=False) if getattr(opts, 'identifiers', None): val = {k.strip():v.strip() for k, v in (x.partition(':')[0::2] for x in opts.identifiers)} if val: orig = mi.get_identifiers() orig.update(val) val = {k:v for k, v in iteritems(orig) if k and v} mi.set_identifiers(val) if getattr(opts, 'cover', None) is not None: ext = os.path.splitext(opts.cover)[1].replace('.', '').upper() mi.cover_data = (ext, open(opts.cover, 'rb').read()) with force_identifiers: set_metadata(stream, mi, stream_type) def main(args=sys.argv): parser = option_parser() opts, args = parser.parse_args(list(map(normalize, args))) if len(args) < 2: parser.print_help() prints(_('No file specified'), file=sys.stderr) return 1 path = args[1] stream_type = os.path.splitext(path)[1].replace('.', '').lower() trying_to_set = False for pref in config().option_set.preferences: if pref.name in ('to_opf', 'get_cover'): continue if getattr(opts, pref.name) is not None: trying_to_set = True break with open(path, 'rb') as stream: mi = get_metadata(stream, stream_type, force_read_metadata=True) if trying_to_set: prints(_('Original metadata')+'::') metadata = str(mi) if trying_to_set: metadata = '\t'+'\n\t'.join(metadata.split('\n')) prints(metadata) if trying_to_set: with open(path, 'r+b') as stream: do_set_metadata(opts, mi, stream, stream_type) stream.seek(0) stream.flush() lrf = None if stream_type == 'lrf': if opts.lrf_bookid is not None: lrf = LRFMetaFile(stream) lrf.book_id = opts.lrf_bookid mi = get_metadata(stream, stream_type, force_read_metadata=True) prints('\n' + _('Changed metadata') + '::') metadata = str(mi) metadata = '\t'+'\n\t'.join(metadata.split('\n')) prints(metadata) if lrf is not None: prints('\tBookID:', lrf.book_id) if opts.to_opf is not None: from calibre.ebooks.metadata.opf2 import OPFCreator opf = OPFCreator(os.getcwd(), mi) with open(opts.to_opf, 'wb') as f: opf.render(f) prints(_('OPF created in'), opts.to_opf) if opts.get_cover is not None: if mi.cover_data and mi.cover_data[1]: with open(opts.get_cover, 'wb') as f: f.write(mi.cover_data[1]) prints(_('Cover saved to'), f.name) else: prints(_('No cover found'), file=sys.stderr) return 0 if __name__ == '__main__': sys.exit(main())