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''' '''Read meta information from epub files'''
import os, re import os, re, posixpath
from cStringIO import StringIO from cStringIO import StringIO
from contextlib import closing from contextlib import closing
@ -126,7 +126,6 @@ class OCFDirReader(OCFReader):
return open(os.path.join(self.root, path), *args, **kwargs) return open(os.path.join(self.root, path), *args, **kwargs)
def get_cover(opf, opf_path, stream, reader=None): def get_cover(opf, opf_path, stream, reader=None):
import posixpath
from calibre.ebooks import render_html_svg_workaround from calibre.ebooks import render_html_svg_workaround
from calibre.utils.logging import default_log from calibre.utils.logging import default_log
raster_cover = opf.raster_cover 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): def set_metadata(stream, mi, apply_null=False, update_timestamp=False):
stream.seek(0) stream.seek(0)
reader = OCFZipReader(stream, root=os.getcwdu()) reader = OCFZipReader(stream, root=os.getcwdu())
raster_cover = reader.opf.raster_cover
mi = MetaInformation(mi) 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'): for x in ('guide', 'toc', 'manifest', 'spine'):
setattr(mi, x, None) setattr(mi, x, None)
reader.opf.smart_update(mi) 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 reader.opf.timestamp = mi.timestamp
newopf = StringIO(reader.opf.render()) 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 = TemporaryFile()
fmt.write(raw) fmt.write(raw)
fmt.seek(0) 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') 'epub')
fmt.seek(0) fmt.seek(0)
mt = guess_type('dummy.'+format.lower())[0] mt = guess_type('dummy.'+format.lower())[0]

View File

@ -1362,7 +1362,7 @@ class ZipFile:
self.fp.close() self.fp.close()
self.fp = None 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 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` 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 zipstream: Stream from a zip file
:param name: The name of the file to replace :param name: The name of the file to replace
:param datastream: The data to replace the file with. :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') z = ZipFile(zipstream, 'r')
replacements = {name:datastream}
replacements.update(extra_replacements)
names = frozenset(replacements.keys())
with SpooledTemporaryFile(max_size=100*1024*1024) as temp: with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
ztemp = ZipFile(temp, 'w') ztemp = ZipFile(temp, 'w')
for obj in z.infolist(): for obj in z.infolist():
if obj.filename == name: if obj.filename in names:
ztemp.writestr(obj, datastream.read()) ztemp.writestr(obj, replacements[obj.filename].read())
else: else:
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True) ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
ztemp.close() ztemp.close()