mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement set_metadata_opf3()
This commit is contained in:
parent
b1678fa7ac
commit
c540e26456
@ -5,7 +5,10 @@
|
|||||||
from __future__ import (unicode_literals, division, absolute_import,
|
from __future__ import (unicode_literals, division, absolute_import,
|
||||||
print_function)
|
print_function)
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
|
||||||
from calibre.ebooks.metadata.opf2 import OPF, pretty_print
|
from calibre.ebooks.metadata.opf2 import OPF, pretty_print
|
||||||
|
from calibre.ebooks.metadata.opf3 import apply_metadata
|
||||||
from calibre.ebooks.metadata.utils import parse_opf, normalize_languages, create_manifest_item, parse_opf_version
|
from calibre.ebooks.metadata.utils import parse_opf, normalize_languages, create_manifest_item, parse_opf_version
|
||||||
from calibre.ebooks.metadata import MetaInformation
|
from calibre.ebooks.metadata import MetaInformation
|
||||||
|
|
||||||
@ -72,6 +75,14 @@ def set_metadata_opf2(root, cover_prefix, mi, opf_version,
|
|||||||
with pretty_print:
|
with pretty_print:
|
||||||
return opf.render(), raster_cover
|
return opf.render(), raster_cover
|
||||||
|
|
||||||
|
def set_metadata_opf3(root, cover_prefix, mi, opf_version,
|
||||||
|
cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
||||||
|
raster_cover = apply_metadata(
|
||||||
|
root, mi, cover_prefix=cover_prefix, cover_data=cover_data,
|
||||||
|
apply_null=apply_null, update_timestamp=update_timestamp,
|
||||||
|
force_identifiers=force_identifiers, add_missing_cover=add_missing_cover)
|
||||||
|
return etree.tostring(root, encoding='utf-8'), raster_cover
|
||||||
|
|
||||||
def set_metadata(stream, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
def set_metadata(stream, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
||||||
if isinstance(stream, bytes):
|
if isinstance(stream, bytes):
|
||||||
stream = DummyFile(stream)
|
stream = DummyFile(stream)
|
||||||
|
@ -15,7 +15,7 @@ from calibre import prints
|
|||||||
from calibre.ebooks.metadata import check_isbn, authors_to_string, string_to_authors
|
from calibre.ebooks.metadata import check_isbn, authors_to_string, string_to_authors
|
||||||
from calibre.ebooks.metadata.book.base import Metadata
|
from calibre.ebooks.metadata.book.base import Metadata
|
||||||
from calibre.ebooks.metadata.book.json_codec import object_to_unicode, decode_is_multiple, encode_is_multiple
|
from calibre.ebooks.metadata.book.json_codec import object_to_unicode, decode_is_multiple, encode_is_multiple
|
||||||
from calibre.ebooks.metadata.utils import parse_opf, pretty_print_opf, ensure_unique, normalize_languages
|
from calibre.ebooks.metadata.utils import parse_opf, pretty_print_opf, ensure_unique, normalize_languages, create_manifest_item
|
||||||
from calibre.ebooks.oeb.base import OPF2_NSMAP, OPF, DC
|
from calibre.ebooks.oeb.base import OPF2_NSMAP, OPF, DC
|
||||||
from calibre.utils.config import from_json, to_json
|
from calibre.utils.config import from_json, to_json
|
||||||
from calibre.utils.date import parse_date as parse_date_, fix_only_date, is_date_undefined, isoformat
|
from calibre.utils.date import parse_date as parse_date_, fix_only_date, is_date_undefined, isoformat
|
||||||
@ -785,6 +785,44 @@ def set_user_metadata(root, prefixes, refines, val):
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
# Covers {{{
|
||||||
|
|
||||||
|
def read_raster_cover(root, prefixes, refines):
|
||||||
|
|
||||||
|
def get_href(item):
|
||||||
|
mt = item.get('media-type')
|
||||||
|
if mt and 'xml' not in mt and 'html' not in mt:
|
||||||
|
href = item.get('href')
|
||||||
|
if href:
|
||||||
|
return href
|
||||||
|
|
||||||
|
for item in items_with_property(root, 'cover-image'):
|
||||||
|
href = get_href(item)
|
||||||
|
if href:
|
||||||
|
return href
|
||||||
|
|
||||||
|
for item_id in XPath('./opf:metadata/opf:meta[@name="cover"]/@content')(root):
|
||||||
|
for item in XPath('./opf:manifest/opf:item[@id and @href and @media-type]')(root):
|
||||||
|
if item.get('id') == item_id:
|
||||||
|
href = get_href(item)
|
||||||
|
if href:
|
||||||
|
return href
|
||||||
|
|
||||||
|
def ensure_is_only_raster_cover(root, prefixes, refines, raster_cover_item_href):
|
||||||
|
for item in XPath('./opf:metadata/opf:meta[@name="cover"]')(root):
|
||||||
|
remove_element(item, refines)
|
||||||
|
for item in items_with_property(root, 'cover-image'):
|
||||||
|
prop = normalize_whitespace(item.get('properties').replace('cover-image', ''))
|
||||||
|
if prop:
|
||||||
|
item.set('properties', prop)
|
||||||
|
else:
|
||||||
|
del item.attrib['properties']
|
||||||
|
for item in XPath('./opf:manifest/opf:item')(root):
|
||||||
|
if item.get('href') == raster_cover_item_href:
|
||||||
|
item.set('properties', normalize_whitespace((item.get('properties') or '') + ' cover-image'))
|
||||||
|
|
||||||
|
# }}}
|
||||||
|
|
||||||
# Reading/setting Metadata objects {{{
|
# Reading/setting Metadata objects {{{
|
||||||
|
|
||||||
def read_metadata(root):
|
def read_metadata(root):
|
||||||
@ -837,7 +875,7 @@ def get_metadata(stream):
|
|||||||
root = parse_opf(stream)
|
root = parse_opf(stream)
|
||||||
return read_metadata(root)
|
return read_metadata(root)
|
||||||
|
|
||||||
def apply_metadata(root, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False):
|
def apply_metadata(root, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
||||||
prefixes, refines = read_prefixes(root), read_refines(root)
|
prefixes, refines = read_prefixes(root), read_refines(root)
|
||||||
current_mi = read_metadata(root)
|
current_mi = read_metadata(root)
|
||||||
if apply_null:
|
if apply_null:
|
||||||
@ -907,8 +945,18 @@ def apply_metadata(root, mi, cover_prefix='', cover_data=None, apply_null=False,
|
|||||||
current_user_metadata[key] = meta
|
current_user_metadata[key] = meta
|
||||||
|
|
||||||
set_user_metadata(root, prefixes, refines, current_user_metadata)
|
set_user_metadata(root, prefixes, refines, current_user_metadata)
|
||||||
|
raster_cover = read_raster_cover(root, prefixes, refines)
|
||||||
|
if not raster_cover and cover_data and add_missing_cover:
|
||||||
|
if cover_prefix and not cover_prefix.endswith('/'):
|
||||||
|
cover_prefix += '/'
|
||||||
|
name = cover_prefix + 'cover.jpg'
|
||||||
|
i = create_manifest_item(root, name, 'cover')
|
||||||
|
if i is not None:
|
||||||
|
ensure_is_only_raster_cover(root, prefixes, refines, name)
|
||||||
|
raster_cover = name
|
||||||
|
|
||||||
pretty_print_opf(root)
|
pretty_print_opf(root)
|
||||||
|
return raster_cover
|
||||||
|
|
||||||
def set_metadata(stream, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
def set_metadata(stream, mi, cover_prefix='', cover_data=None, apply_null=False, update_timestamp=False, force_identifiers=False, add_missing_cover=True):
|
||||||
root = parse_opf(stream)
|
root = parse_opf(stream)
|
||||||
@ -918,44 +966,6 @@ def set_metadata(stream, mi, cover_prefix='', cover_data=None, apply_null=False,
|
|||||||
force_identifiers=force_identifiers)
|
force_identifiers=force_identifiers)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Covers {{{
|
|
||||||
|
|
||||||
def raster_cover(root):
|
|
||||||
|
|
||||||
def get_href(item):
|
|
||||||
mt = item.get('media-type')
|
|
||||||
if mt and 'xml' not in mt and 'html' not in mt:
|
|
||||||
href = item.get('href')
|
|
||||||
if href:
|
|
||||||
return href
|
|
||||||
|
|
||||||
for item in items_with_property(root, 'cover-image'):
|
|
||||||
href = get_href(item)
|
|
||||||
if href:
|
|
||||||
return href
|
|
||||||
|
|
||||||
for item_id in XPath('./opf:metadata/opf:meta[@name="cover"]/@content')(root):
|
|
||||||
for item in XPath('./opf:manifest/opf:item[@id and @href and @media-type]')(root):
|
|
||||||
if item.get('id') == item_id:
|
|
||||||
href = get_href(item)
|
|
||||||
if href:
|
|
||||||
return href
|
|
||||||
|
|
||||||
def ensure_is_only_raster_cover(root, raster_cover_item_href):
|
|
||||||
refines = read_refines(root)
|
|
||||||
for item in XPath('./opf:metadata/opf:meta[@name="cover"]')(root):
|
|
||||||
remove_element(item, refines)
|
|
||||||
for item in items_with_property(root, 'cover-image'):
|
|
||||||
prop = normalize_whitespace(item.get('properties').replace('cover-image', ''))
|
|
||||||
if prop:
|
|
||||||
item.set('properties', prop)
|
|
||||||
else:
|
|
||||||
del item.attrib['properties']
|
|
||||||
for item in XPath('./opf:manifest/opf:item')(root):
|
|
||||||
if item.get('href') == raster_cover_item_href:
|
|
||||||
item.set('properties', normalize_whitespace((item.get('properties') or '') + ' cover-image'))
|
|
||||||
|
|
||||||
# }}}
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import sys
|
import sys
|
||||||
|
@ -22,7 +22,7 @@ from calibre.ebooks.metadata.opf3 import (
|
|||||||
set_comments, read_publisher, set_publisher, read_tags, set_tags, read_rating,
|
set_comments, read_publisher, set_publisher, read_tags, set_tags, read_rating,
|
||||||
set_rating, read_series, set_series, read_user_metadata, set_user_metadata,
|
set_rating, read_series, set_series, read_user_metadata, set_user_metadata,
|
||||||
read_author_link_map, read_user_categories, set_author_link_map, set_user_categories,
|
read_author_link_map, read_user_categories, set_author_link_map, set_user_categories,
|
||||||
apply_metadata, raster_cover, ensure_is_only_raster_cover
|
apply_metadata, read_raster_cover, ensure_is_only_raster_cover
|
||||||
)
|
)
|
||||||
# This import is needed to prevent a test from running slowly
|
# This import is needed to prevent a test from running slowly
|
||||||
from calibre.ebooks.oeb.polish.pretty import pretty_opf, pretty_xml_tree # noqa
|
from calibre.ebooks.oeb.polish.pretty import pretty_opf, pretty_xml_tree # noqa
|
||||||
@ -207,13 +207,13 @@ class TestOPF3(unittest.TestCase):
|
|||||||
|
|
||||||
def test_raster_cover(self): # {{{
|
def test_raster_cover(self): # {{{
|
||||||
def rt(root):
|
def rt(root):
|
||||||
return raster_cover(root)
|
return read_raster_cover(root, read_prefixes(root), read_refines(root))
|
||||||
root = self.get_opf('<meta name="cover" content="cover"/>', '<item id="cover" media-type="image/jpeg" href="x.jpg"/>')
|
root = self.get_opf('<meta name="cover" content="cover"/>', '<item id="cover" media-type="image/jpeg" href="x.jpg"/>')
|
||||||
self.ae('x.jpg', rt(root))
|
self.ae('x.jpg', rt(root))
|
||||||
root = self.get_opf('<meta name="cover" content="cover"/>',
|
root = self.get_opf('<meta name="cover" content="cover"/>',
|
||||||
'<item id="cover" media-type="image/jpeg" href="x.jpg"/><item media-type="image/jpeg" href="y.jpg" properties="cover-image"/>')
|
'<item id="cover" media-type="image/jpeg" href="x.jpg"/><item media-type="image/jpeg" href="y.jpg" properties="cover-image"/>')
|
||||||
self.ae('y.jpg', rt(root))
|
self.ae('y.jpg', rt(root))
|
||||||
ensure_is_only_raster_cover(root, 'x.jpg')
|
ensure_is_only_raster_cover(root, read_prefixes(root), read_refines(root), 'x.jpg')
|
||||||
self.ae('x.jpg', rt(root))
|
self.ae('x.jpg', rt(root))
|
||||||
self.ae(['x.jpg'], root.xpath('//*[@properties="cover-image"]/@href'))
|
self.ae(['x.jpg'], root.xpath('//*[@properties="cover-image"]/@href'))
|
||||||
self.assertFalse(root.xpath('//*[@name]'))
|
self.assertFalse(root.xpath('//*[@name]'))
|
||||||
@ -486,7 +486,7 @@ class TestOPF3(unittest.TestCase):
|
|||||||
"label": "date", "table":
|
"label": "date", "table":
|
||||||
"custom_column_2", "is_multiple": null,
|
"custom_column_2", "is_multiple": null,
|
||||||
"is_category": false}"/>
|
"is_category": false}"/>
|
||||||
</metadata>
|
</metadata><manifest/>
|
||||||
</package>''' # }}}
|
</package>''' # }}}
|
||||||
|
|
||||||
def compare_metadata(mi2, mi3):
|
def compare_metadata(mi2, mi3):
|
||||||
@ -520,6 +520,8 @@ class TestOPF3(unittest.TestCase):
|
|||||||
self.assertFalse(nmi.tags)
|
self.assertFalse(nmi.tags)
|
||||||
self.assertFalse(nmi.get('#tags'))
|
self.assertFalse(nmi.get('#tags'))
|
||||||
self.assertFalse(nmi.get('#commetns'))
|
self.assertFalse(nmi.get('#commetns'))
|
||||||
|
self.assertIsNone(apply_metadata(root, mi3, cover_data=b'x', cover_prefix='xxx', add_missing_cover=False))
|
||||||
|
self.ae('xxx/cover.jpg', apply_metadata(root, mi3, cover_data=b'x', cover_prefix='xxx'))
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
# Run tests {{{
|
# Run tests {{{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user