EPUB metadata: When setting metadata in an EPUB file, if it has a well defined image based cover, update it

This commit is contained in:
Kovid Goyal 2010-07-29 22:42:21 -06:00
parent 1b3a799d0d
commit d7d9fa4141
3 changed files with 44 additions and 7 deletions

View File

@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
'''Read meta information from epub files'''
import os, re
import os, re, posixpath
from cStringIO import StringIO
from contextlib import closing
@ -126,7 +126,6 @@ class OCFDirReader(OCFReader):
return open(os.path.join(self.root, path), *args, **kwargs)
def get_cover(opf, opf_path, stream, reader=None):
import posixpath
from calibre.ebooks import render_html_svg_workaround
from calibre.utils.logging import default_log
raster_cover = opf.raster_cover
@ -185,7 +184,37 @@ def get_quick_metadata(stream):
def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
stream.seek(0)
reader = OCFZipReader(stream, root=os.getcwdu())
raster_cover = reader.opf.raster_cover
mi = MetaInformation(mi)
new_cdata = None
replacements = {}
try:
new_cdata = mi.cover_data[1]
if not new_cdata:
raise Exception('no cover')
except:
try:
new_cdata = open(mi.cover, 'rb').read()
except:
import traceback
traceback.print_exc()
if new_cdata and raster_cover:
try:
cpath = posixpath.join(posixpath.dirname(reader.opf_path),
raster_cover)
cover_replacable = not reader.encryption_meta.is_encrypted(cpath) and \
os.path.splitext(cpath)[1].lower() in ('.png', '.jpg', '.jpeg')
if cover_replacable:
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.magick_draw import save_cover_data_to
new_cover = PersistentTemporaryFile(suffix=os.path.splitext(cpath)[1])
new_cover.close()
save_cover_data_to(new_cdata, new_cover.name)
replacements[cpath] = open(new_cover.name, 'rb')
except:
import traceback
traceback.print_exc()
for x in ('guide', 'toc', 'manifest', 'spine'):
setattr(mi, x, None)
reader.opf.smart_update(mi)
@ -200,5 +229,6 @@ def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
reader.opf.timestamp = mi.timestamp
newopf = StringIO(reader.opf.render())
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf)
safe_replace(stream, reader.container[OPF.MIMETYPE], newopf,
extra_replacements=replacements)

View File

@ -183,7 +183,8 @@ class ContentServer(object):
fmt = TemporaryFile()
fmt.write(raw)
fmt.seek(0)
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True),
set_metadata(fmt, self.db.get_metadata(id, index_is_id=True,
get_cover=True),
'epub')
fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0]

View File

@ -1362,7 +1362,7 @@ class ZipFile:
self.fp.close()
self.fp = None
def safe_replace(zipstream, name, datastream):
def safe_replace(zipstream, name, datastream, extra_replacements={}):
'''
Replace a file in a zip file in a safe manner. This proceeds by extracting
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
@ -1371,13 +1371,19 @@ def safe_replace(zipstream, name, datastream):
:param zipstream: Stream from a zip file
:param name: The name of the file to replace
:param datastream: The data to replace the file with.
:param extra_replacements: Extra replacements. Mapping of name to file-like
objects
'''
z = ZipFile(zipstream, 'r')
replacements = {name:datastream}
replacements.update(extra_replacements)
names = frozenset(replacements.keys())
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
ztemp = ZipFile(temp, 'w')
for obj in z.infolist():
if obj.filename == name:
ztemp.writestr(obj, datastream.read())
if obj.filename in names:
ztemp.writestr(obj, replacements[obj.filename].read())
else:
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
ztemp.close()