Allow setting remaining standard calibre metadata in ODT files

This commit is contained in:
Kovid Goyal 2019-09-17 15:29:28 +05:30
parent d2eca068fc
commit 9cc6842006
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -21,6 +21,7 @@
from __future__ import absolute_import, division, print_function, unicode_literals from __future__ import absolute_import, division, print_function, unicode_literals
import io import io
import json
import os import os
import re import re
@ -29,13 +30,14 @@ from lxml.etree import fromstring, tostring
from calibre.ebooks.metadata import ( from calibre.ebooks.metadata import (
MetaInformation, authors_to_string, check_isbn, string_to_authors MetaInformation, authors_to_string, check_isbn, string_to_authors
) )
from calibre.utils.date import parse_date from calibre.utils.date import isoformat, parse_date
from calibre.utils.imghdr import identify from calibre.utils.imghdr import identify
from calibre.utils.localization import canonicalize_lang, lang_as_iso639_1 from calibre.utils.localization import canonicalize_lang, lang_as_iso639_1
from calibre.utils.zipfile import ZipFile, safe_replace from calibre.utils.zipfile import ZipFile, safe_replace
from odf.draw import Frame as odFrame, Image as odImage from odf.draw import Frame as odFrame, Image as odImage
from odf.namespaces import DCNS, METANS, OFFICENS from odf.namespaces import DCNS, METANS, OFFICENS
from odf.opendocument import load as odLoad from odf.opendocument import load as odLoad
from polyglot.builtins import as_unicode
fields = { fields = {
'title': (DCNS, 'title'), 'title': (DCNS, 'title'),
@ -117,6 +119,16 @@ def get_metadata(stream, extract_cover=True):
mi.publisher = data['opf.publisher'] mi.publisher = data['opf.publisher']
if data.get('opf.pubdate', ''): if data.get('opf.pubdate', ''):
mi.pubdate = parse_date(data['opf.pubdate'], assume_utc=True) mi.pubdate = parse_date(data['opf.pubdate'], assume_utc=True)
if data.get('opf.identifiers'):
try:
mi.identifiers = json.loads(data['opf.identifiers'])
except Exception:
pass
if data.get('opf.rating'):
try:
mi.rating = max(0, min(float(data['opf.rating']), 10))
except Exception:
pass
if data.get('opf.series', ''): if data.get('opf.series', ''):
mi.series = data['opf.series'] mi.series = data['opf.series']
if data.get('opf.seriesindex', ''): if data.get('opf.seriesindex', ''):
@ -142,6 +154,7 @@ def set_metadata(stream, mi):
with ZipFile(stream) as zf: with ZipFile(stream) as zf:
raw = _set_metadata(zf.open('meta.xml').read(), mi) raw = _set_metadata(zf.open('meta.xml').read(), mi)
# print(raw.decode('utf-8'))
stream.seek(os.SEEK_SET) stream.seek(os.SEEK_SET)
safe_replace(stream, "meta.xml", io.BytesIO(raw)) safe_replace(stream, "meta.xml", io.BytesIO(raw))
@ -168,15 +181,45 @@ def _set_metadata(raw, mi):
meta.append(ans) meta.append(ans)
return ans return ans
def remove_user_metadata(*names):
for x in xpath('//meta:user-defined'):
q = (x.get('{%s}name' % METANS) or '').lower()
if q in names:
x.getparent().remove(x)
def add_um(name, val, vtype='string'):
ans = add('user-defined', val)
ans.set('{%s}value-type' % METANS, vtype)
ans.set('{%s}name' % METANS, name)
def add_user_metadata(name, val):
if not hasattr(add_user_metadata, 'sentinel_added'):
add_user_metadata.sentinel_added = True
remove_user_metadata('opf.metadata')
add_um('opf.metadata', 'true', 'boolean')
val_type = 'string'
if hasattr(val, 'strftime'):
val = isoformat(val, as_utc=True).split('T')[0]
val_type = 'date'
add_um(name, val, val_type)
meta = xpath('//office:meta')[0] meta = xpath('//office:meta')[0]
if not mi.is_null('title'): if not mi.is_null('title'):
remove('title') remove('title')
add('title', mi.title) add('title', mi.title)
if not mi.is_null('title_sort'):
remove_user_metadata('opf.titlesort')
add_user_metadata('opf.titlesort', mi.title_sort)
if not mi.is_null('authors'): if not mi.is_null('authors'):
remove('initial-creator', 'creator') remove('initial-creator', 'creator')
val = authors_to_string(mi.authors) val = authors_to_string(mi.authors)
add('initial-creator', val), add('creator', val) add('initial-creator', val), add('creator', val)
remove_user_metadata('opf.authors')
add_user_metadata('opf.authors', val)
if not mi.is_null('author_sort'):
remove_user_metadata('opf.authorsort')
add_user_metadata('opf.authorsort', mi.author_sort)
if not mi.is_null('comments'): if not mi.is_null('comments'):
remove('description') remove('description')
add('description', mi.comments) add('description', mi.comments)
@ -188,6 +231,22 @@ def _set_metadata(raw, mi):
if lang: if lang:
remove('language') remove('language')
add('language', lang) add('language', lang)
if not mi.is_null('pubdate'):
remove_user_metadata('opf.pubdate')
add_user_metadata('opf.pubdate', mi.pubdate)
if not mi.is_null('publisher'):
remove_user_metadata('opf.publisher')
add_user_metadata('opf.publisher', mi.publisher)
if not mi.is_null('series'):
remove_user_metadata('opf.series', 'opf.seriesindex')
add_user_metadata('opf.series', mi.series)
add_user_metadata('opf.seriesindex', '{}'.format(mi.series_index))
if not mi.is_null('identifiers'):
remove_user_metadata('opf.identifiers')
add_user_metadata('opf.identifiers', as_unicode(json.dumps(mi.identifiers)))
if not mi.is_null('rating'):
remove_user_metadata('opf.rating')
add_user_metadata('opf.rating', '%.2g' % mi.rating)
return tostring(root, encoding='utf-8', pretty_print=True) return tostring(root, encoding='utf-8', pretty_print=True)