EPUB Metadata: Implement updating identifiers

EPUB Metadata: Implementing updating identifiers in the epub file from
calibre when polishing or exporting the epub

ebook-meta: Add an --identifier option to set identifiers.
This commit is contained in:
Kovid Goyal 2013-10-11 09:48:58 +05:30
parent 61f5306019
commit 91e93871b1
5 changed files with 68 additions and 8 deletions

View File

@ -435,7 +435,7 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
def set_metadata(self, stream, mi, type):
from calibre.ebooks.metadata.epub import set_metadata
set_metadata(stream, mi, apply_null=self.apply_null)
set_metadata(stream, mi, apply_null=self.apply_null, force_identifiers=self.force_identifiers)
class FB2MetadataWriter(MetadataWriterPlugin):
@ -1695,7 +1695,7 @@ class StoreWHSmithUKStore(StoreBase):
class StoreWolneLekturyStore(StoreBase):
name = 'Wolne Lektury'
author = u'Tomasz Długosz'
description = u'Wolne Lektury to biblioteka internetowa czynna 24 godziny na dobę, 365 dni w roku, której zasoby dostępne są całkowicie za darmo. Wszystkie dzieła są odpowiednio opracowane - opatrzone przypisami, motywami i udostępnione w kilku formatach - HTML, TXT, PDF, EPUB, MOBI, FB2.'
description = u'Wolne Lektury to biblioteka internetowa czynna 24 godziny na dobę, 365 dni w roku, której zasoby dostępne są całkowicie za darmo. Wszystkie dzieła są odpowiednio opracowane - opatrzone przypisami, motywami i udostępnione w kilku formatach - HTML, TXT, PDF, EPUB, MOBI, FB2.' # noqa
actual_plugin = 'calibre.gui2.store.stores.wolnelektury_plugin:WolneLekturyStore'
headquarters = 'PL'

View File

@ -319,6 +319,19 @@ class ApplyNullMetadata(object):
apply_null_metadata = ApplyNullMetadata()
class ForceIdentifiers(object):
def __init__(self):
self.force_identifiers = False
def __enter__(self):
self.force_identifiers = True
def __exit__(self, *args):
self.force_identifiers = False
force_identifiers = ForceIdentifiers()
def get_file_type_metadata(stream, ftype):
mi = MetaInformation(None, None)
@ -346,6 +359,7 @@ def set_file_type_metadata(stream, mi, ftype):
with plugin:
try:
plugin.apply_null = apply_null_metadata.apply_null
plugin.force_identifiers = force_identifiers.force_identifiers
plugin.set_metadata(stream, mi, ftype.lower().strip())
break
except:

View File

@ -9,7 +9,7 @@ ebook-meta
import sys, os
from calibre.utils.config import StringConfig
from calibre.customize.ui import metadata_readers, metadata_writers
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
@ -63,6 +63,11 @@ def config():
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:http://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'],
@ -114,7 +119,7 @@ def do_set_metadata(opts, mi, stream, stream_type):
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'):
'lrf_bookid', 'identifiers'):
continue
val = getattr(opts, pref.name, None)
if val is not None:
@ -136,11 +141,19 @@ def do_set_metadata(opts, mi, stream, stream_type):
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 orig.iteritems() 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)

View File

@ -250,7 +250,7 @@ def _write_new_cover(new_cdata, cpath):
save_cover_data_to(new_cdata, new_cover.name)
return new_cover
def update_metadata(opf, mi, apply_null=False, update_timestamp=False):
def update_metadata(opf, mi, apply_null=False, update_timestamp=False, force_identifiers=False):
for x in ('guide', 'toc', 'manifest', 'spine'):
setattr(mi, x, None)
if mi.languages:
@ -274,10 +274,16 @@ def update_metadata(opf, mi, apply_null=False, update_timestamp=False):
opf.isbn = None
if not getattr(mi, 'comments', None):
opf.comments = None
if apply_null or force_identifiers:
opf.set_identifiers(mi.get_identifiers())
else:
orig = opf.get_identifiers()
orig.update(mi.get_identifiers())
opf.set_identifiers({k:v for k, v in orig.iteritems() if k and v})
if update_timestamp and mi.timestamp is not None:
opf.timestamp = mi.timestamp
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
def set_metadata(stream, mi, apply_null=False, update_timestamp=False, force_identifiers=False):
stream.seek(0)
reader = get_zip_reader(stream, root=os.getcwdu())
raster_cover = reader.opf.raster_cover
@ -308,7 +314,7 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
traceback.print_exc()
update_metadata(reader.opf, mi, apply_null=apply_null,
update_timestamp=update_timestamp)
update_timestamp=update_timestamp, force_identifiers=force_identifiers)
newopf = StringIO(reader.opf.render())
if isinstance(reader.archive, LocalZipFile):

View File

@ -959,6 +959,33 @@ class OPF(object): # {{{
identifiers['isbn'] = val
return identifiers
def set_identifiers(self, identifiers):
identifiers = identifiers.copy()
uuid_id = None
for attr in self.root.attrib:
if attr.endswith('unique-identifier'):
uuid_id = self.root.attrib[attr]
break
for x in self.XPath(
'descendant::*[local-name() = "identifier"]')(
self.metadata):
xid = x.get('id', None)
is_package_identifier = uuid_id is not None and uuid_id == xid
typ = {val for attr, val in x.attrib.iteritems() if attr.endswith('scheme')}
if is_package_identifier:
typ = tuple(typ)
if typ and typ[0].lower() in identifiers:
self.set_text(x, identifiers.pop(typ[0].lower()))
continue
if typ and not (typ & {'calibre', 'uuid'}):
x.getparent().remove(x)
for typ, val in identifiers.iteritems():
attrib = {'{%s}scheme'%self.NAMESPACES['opf']: typ.upper()}
self.set_text(self.create_metadata_element(
'identifier', attrib=attrib), unicode(val))
@dynamic_property
def application_id(self):