mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
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:
parent
61f5306019
commit
91e93871b1
@ -435,7 +435,7 @@ class EPUBMetadataWriter(MetadataWriterPlugin):
|
|||||||
|
|
||||||
def set_metadata(self, stream, mi, type):
|
def set_metadata(self, stream, mi, type):
|
||||||
from calibre.ebooks.metadata.epub import set_metadata
|
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):
|
class FB2MetadataWriter(MetadataWriterPlugin):
|
||||||
|
|
||||||
@ -1695,7 +1695,7 @@ class StoreWHSmithUKStore(StoreBase):
|
|||||||
class StoreWolneLekturyStore(StoreBase):
|
class StoreWolneLekturyStore(StoreBase):
|
||||||
name = 'Wolne Lektury'
|
name = 'Wolne Lektury'
|
||||||
author = u'Tomasz Długosz'
|
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'
|
actual_plugin = 'calibre.gui2.store.stores.wolnelektury_plugin:WolneLekturyStore'
|
||||||
|
|
||||||
headquarters = 'PL'
|
headquarters = 'PL'
|
||||||
|
@ -319,6 +319,19 @@ class ApplyNullMetadata(object):
|
|||||||
|
|
||||||
apply_null_metadata = ApplyNullMetadata()
|
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):
|
def get_file_type_metadata(stream, ftype):
|
||||||
mi = MetaInformation(None, None)
|
mi = MetaInformation(None, None)
|
||||||
|
|
||||||
@ -346,6 +359,7 @@ def set_file_type_metadata(stream, mi, ftype):
|
|||||||
with plugin:
|
with plugin:
|
||||||
try:
|
try:
|
||||||
plugin.apply_null = apply_null_metadata.apply_null
|
plugin.apply_null = apply_null_metadata.apply_null
|
||||||
|
plugin.force_identifiers = force_identifiers.force_identifiers
|
||||||
plugin.set_metadata(stream, mi, ftype.lower().strip())
|
plugin.set_metadata(stream, mi, ftype.lower().strip())
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
|
@ -9,7 +9,7 @@ ebook-meta
|
|||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
from calibre.utils.config import StringConfig
|
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.meta import get_metadata, set_metadata
|
||||||
from calibre.ebooks.metadata import string_to_authors, authors_to_sort_string, \
|
from calibre.ebooks.metadata import string_to_authors, authors_to_sort_string, \
|
||||||
title_sort, MetaInformation
|
title_sort, MetaInformation
|
||||||
@ -63,6 +63,11 @@ def config():
|
|||||||
help=_('Set the rating. Should be a number between 1 and 5.'))
|
help=_('Set the rating. Should be a number between 1 and 5.'))
|
||||||
c.add_opt('isbn', ['--isbn'],
|
c.add_opt('isbn', ['--isbn'],
|
||||||
help=_('Set the ISBN of the book.'))
|
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'],
|
c.add_opt('tags', ['--tags'],
|
||||||
help=_('Set the tags for the book. Should be a comma separated list.'))
|
help=_('Set the tags for the book. Should be a comma separated list.'))
|
||||||
c.add_opt('book_producer', ['-k', '--book-producer'],
|
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:
|
for pref in config().option_set.preferences:
|
||||||
if pref.name in ('to_opf', 'from_opf', 'authors', 'title_sort',
|
if pref.name in ('to_opf', 'from_opf', 'authors', 'title_sort',
|
||||||
'author_sort', 'get_cover', 'cover', 'tags',
|
'author_sort', 'get_cover', 'cover', 'tags',
|
||||||
'lrf_bookid'):
|
'lrf_bookid', 'identifiers'):
|
||||||
continue
|
continue
|
||||||
val = getattr(opts, pref.name, None)
|
val = getattr(opts, pref.name, None)
|
||||||
if val is not 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())
|
mi.series_index = float(opts.series_index.strip())
|
||||||
if getattr(opts, 'pubdate', None) is not None:
|
if getattr(opts, 'pubdate', None) is not None:
|
||||||
mi.pubdate = parse_date(opts.pubdate, assume_utc=False, as_utc=False)
|
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:
|
if getattr(opts, 'cover', None) is not None:
|
||||||
ext = os.path.splitext(opts.cover)[1].replace('.', '').upper()
|
ext = os.path.splitext(opts.cover)[1].replace('.', '').upper()
|
||||||
mi.cover_data = (ext, open(opts.cover, 'rb').read())
|
mi.cover_data = (ext, open(opts.cover, 'rb').read())
|
||||||
|
|
||||||
|
with force_identifiers:
|
||||||
set_metadata(stream, mi, stream_type)
|
set_metadata(stream, mi, stream_type)
|
||||||
|
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ def _write_new_cover(new_cdata, cpath):
|
|||||||
save_cover_data_to(new_cdata, new_cover.name)
|
save_cover_data_to(new_cdata, new_cover.name)
|
||||||
return new_cover
|
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'):
|
for x in ('guide', 'toc', 'manifest', 'spine'):
|
||||||
setattr(mi, x, None)
|
setattr(mi, x, None)
|
||||||
if mi.languages:
|
if mi.languages:
|
||||||
@ -274,10 +274,16 @@ def update_metadata(opf, mi, apply_null=False, update_timestamp=False):
|
|||||||
opf.isbn = None
|
opf.isbn = None
|
||||||
if not getattr(mi, 'comments', None):
|
if not getattr(mi, 'comments', None):
|
||||||
opf.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:
|
if update_timestamp and mi.timestamp is not None:
|
||||||
opf.timestamp = mi.timestamp
|
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)
|
stream.seek(0)
|
||||||
reader = get_zip_reader(stream, root=os.getcwdu())
|
reader = get_zip_reader(stream, root=os.getcwdu())
|
||||||
raster_cover = reader.opf.raster_cover
|
raster_cover = reader.opf.raster_cover
|
||||||
@ -308,7 +314,7 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
update_metadata(reader.opf, mi, apply_null=apply_null,
|
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())
|
newopf = StringIO(reader.opf.render())
|
||||||
if isinstance(reader.archive, LocalZipFile):
|
if isinstance(reader.archive, LocalZipFile):
|
||||||
|
@ -959,6 +959,33 @@ class OPF(object): # {{{
|
|||||||
identifiers['isbn'] = val
|
identifiers['isbn'] = val
|
||||||
return identifiers
|
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
|
@dynamic_property
|
||||||
def application_id(self):
|
def application_id(self):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user