mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 02:34:06 -04:00
Implement serialization of Metadata instances
This commit is contained in:
parent
530cb0d00a
commit
1e4dae7e16
@ -237,6 +237,17 @@ class ReadingTest(BaseTest):
|
||||
self.compare_metadata(mi1, mi2)
|
||||
# }}}
|
||||
|
||||
def test_serialize_metadata(self): # {{{
|
||||
from calibre.utils.serialize import json_dumps, json_loads, msgpack_dumps, msgpack_loads
|
||||
cache = self.init_cache(self.library_path)
|
||||
for i in xrange(1, 4):
|
||||
mi = cache.get_metadata(i, get_cover=True, cover_as_data=True)
|
||||
rmi = msgpack_loads(msgpack_dumps(mi))
|
||||
self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
|
||||
rmi = json_loads(json_dumps(mi))
|
||||
self.compare_metadata(mi, rmi, exclude='format_metadata has_cover formats id'.split())
|
||||
# }}}
|
||||
|
||||
def test_get_cover(self): # {{{
|
||||
'Test cover() returns the same data for both backends'
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
|
66
src/calibre/ebooks/metadata/book/serialize.py
Normal file
66
src/calibre/ebooks/metadata/book/serialize.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python2
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPLv3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import base64
|
||||
|
||||
from calibre.constants import preferred_encoding
|
||||
from calibre.ebooks.metadata.book import SERIALIZABLE_FIELDS
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
from calibre.utils.imghdr import what
|
||||
|
||||
|
||||
def ensure_unicode(obj, enc=preferred_encoding):
|
||||
if isinstance(obj, unicode):
|
||||
return obj
|
||||
if isinstance(obj, bytes):
|
||||
return obj.decode(enc, 'replace')
|
||||
if isinstance(obj, (list, tuple)):
|
||||
return [ensure_unicode(x) for x in obj]
|
||||
if isinstance(obj, dict):
|
||||
return {ensure_unicode(k): ensure_unicode(v) for k, v in obj.iteritems()}
|
||||
return obj
|
||||
|
||||
|
||||
def read_cover(mi):
|
||||
if mi.cover_data and mi.cover_data[1]:
|
||||
return
|
||||
if mi.cover:
|
||||
try:
|
||||
with lopen(mi.cover, 'rb') as f:
|
||||
cd = f.read()
|
||||
mi.cover_data = what(cd), cd
|
||||
except EnvironmentError:
|
||||
pass
|
||||
return mi
|
||||
|
||||
|
||||
def metadata_as_dict(mi, encode_cover_data=False):
|
||||
if hasattr(mi, 'to_book_metadata'):
|
||||
mi = mi.to_book_metadata()
|
||||
ans = {}
|
||||
for field in SERIALIZABLE_FIELDS:
|
||||
if field != 'cover' and not mi.is_null(field):
|
||||
val = getattr(mi, field)
|
||||
ans[field] = ensure_unicode(val)
|
||||
if mi.cover_data and mi.cover_data[1]:
|
||||
if encode_cover_data:
|
||||
ans['cover_data'] = [mi.cover_data[0], base64.standard_b64encode(bytes(mi.cover_data[1]))]
|
||||
else:
|
||||
ans['cover_data'] = mi.cover_data
|
||||
um = mi.get_all_user_metadata(False)
|
||||
if um:
|
||||
ans['user_metadata'] = um
|
||||
return ans
|
||||
|
||||
|
||||
def metadata_from_dict(src):
|
||||
ans = Metadata('Unknown')
|
||||
for key, value in src.iteritems():
|
||||
if key == 'user_metadata':
|
||||
ans.set_all_user_metadata(value)
|
||||
else:
|
||||
setattr(ans, key, value)
|
||||
return ans
|
@ -4,17 +4,23 @@
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import json
|
||||
import json, base64
|
||||
from functools import partial
|
||||
from datetime import datetime
|
||||
from calibre.utils.iso8601 import parse_iso8601
|
||||
|
||||
from calibre.utils.iso8601 import parse_iso8601
|
||||
|
||||
MSGPACK_MIME = 'application/x-msgpack'
|
||||
|
||||
|
||||
def encoder(obj):
|
||||
def encoder(obj, for_json=False):
|
||||
if isinstance(obj, datetime):
|
||||
return {'__datetime__': obj.isoformat()}
|
||||
return {'__datetime__': unicode(obj.isoformat())}
|
||||
if obj.__class__.__name__ == 'Metadata':
|
||||
from calibre.ebooks.metadata.book.base import Metadata
|
||||
if isinstance(obj, Metadata):
|
||||
from calibre.ebooks.metadata.book.serialize import metadata_as_dict
|
||||
obj = {'__metadata__': metadata_as_dict(obj, encode_cover_data=for_json)}
|
||||
return obj
|
||||
|
||||
|
||||
@ -24,7 +30,7 @@ def msgpack_dumps(data):
|
||||
|
||||
|
||||
def json_dumps(data, **kw):
|
||||
kw['default'] = encoder
|
||||
kw['default'] = partial(encoder, for_json=True)
|
||||
kw['ensure_ascii'] = False
|
||||
ans = json.dumps(data, **kw)
|
||||
if not isinstance(ans, bytes):
|
||||
@ -32,19 +38,24 @@ def json_dumps(data, **kw):
|
||||
return ans
|
||||
|
||||
|
||||
def decoder(obj):
|
||||
def decoder(obj, for_json=False):
|
||||
dt = obj.get('__datetime__')
|
||||
if dt is not None:
|
||||
obj = parse_iso8601(dt, assume_utc=True)
|
||||
return parse_iso8601(dt, assume_utc=True)
|
||||
m = obj.get('__metadata__')
|
||||
if m is not None:
|
||||
from calibre.ebooks.metadata.book.serialize import metadata_from_dict
|
||||
obj = metadata_from_dict(m)
|
||||
if for_json and obj.cover_data and obj.cover_data[1]:
|
||||
obj.cover_data = obj.cover_data[0], base64.standard_b64decode(obj.cover_data[1])
|
||||
return obj
|
||||
return obj
|
||||
|
||||
|
||||
def msgpack_loads(data):
|
||||
import msgpack
|
||||
return msgpack.unpackb(
|
||||
data, encoding='utf-8', use_list=False, object_hook=decoder
|
||||
)
|
||||
return msgpack.unpackb(data, encoding='utf-8', object_hook=decoder)
|
||||
|
||||
|
||||
def json_loads(data):
|
||||
return json.loads(data, object_hook=decoder)
|
||||
return json.loads(data, object_hook=partial(decoder, for_json=True))
|
||||
|
Loading…
x
Reference in New Issue
Block a user