mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Make EPUB metadata editing more robust and send LRF files to the 505 in preference to EPUB
This commit is contained in:
parent
deebf85442
commit
c3611c0918
@ -31,7 +31,7 @@ class PRS505(Device):
|
||||
PRODUCT_ID = 0x031e #: Product Id for the PRS-505
|
||||
PRODUCT_NAME = 'PRS-505'
|
||||
VENDOR_NAME = 'SONY'
|
||||
FORMATS = ["lrf", 'epub', "rtf", "pdf", "txt"]
|
||||
FORMATS = ['lrf', 'epub', "rtf", "pdf", "txt"]
|
||||
|
||||
MEDIA_XML = 'database/cache/media.xml'
|
||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||
|
@ -22,6 +22,7 @@ __docformat__ = "epytext"
|
||||
preferred_source_formats = [
|
||||
'LIT',
|
||||
'MOBI',
|
||||
'EPUB',
|
||||
'HTML',
|
||||
'HTM',
|
||||
'XHTM',
|
||||
|
@ -7,7 +7,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
import sys, os
|
||||
|
||||
from calibre.utils.zipfile import ZipFile, BadZipfile
|
||||
from calibre.utils.zipfile import ZipFile, BadZipfile, safe_replace
|
||||
from cStringIO import StringIO
|
||||
from contextlib import closing
|
||||
|
||||
@ -73,7 +73,7 @@ class OCFReader(OCF):
|
||||
class OCFZipReader(OCFReader):
|
||||
def __init__(self, stream, mode='r'):
|
||||
try:
|
||||
self.archive = ZipFile(stream, mode)
|
||||
self.archive = ZipFile(stream, mode=mode)
|
||||
except BadZipfile:
|
||||
raise EPubException("not a ZIP .epub OCF container")
|
||||
self.root = getattr(stream, 'name', os.getcwd())
|
||||
@ -82,18 +82,20 @@ class OCFZipReader(OCFReader):
|
||||
def open(self, name, mode='r'):
|
||||
return StringIO(self.archive.read(name))
|
||||
|
||||
class OCFZipWriter(OCFZipReader):
|
||||
class OCFZipWriter(object):
|
||||
|
||||
def __init__(self, stream):
|
||||
OCFZipReader.__init__(self, stream, mode='a')
|
||||
reader = OCFZipReader(stream)
|
||||
self.opf = reader.container[OPF.MIMETYPE]
|
||||
self.stream = stream
|
||||
self.root = getattr(stream, 'name', os.getcwd())
|
||||
|
||||
def set_metadata(self, mi):
|
||||
name = self.container[OPF.MIMETYPE]
|
||||
stream = StringIO()
|
||||
opf = OPFCreator(self.root, mi)
|
||||
opf.render(stream)
|
||||
self.archive.delete(name)
|
||||
self.archive.writestr(name, stream.getvalue())
|
||||
stream.seek(0)
|
||||
safe_replace(self.stream, self.opf, stream)
|
||||
|
||||
class OCFDirReader(OCFReader):
|
||||
def __init__(self, path):
|
||||
@ -133,9 +135,8 @@ def main(args=sys.argv):
|
||||
mi.tags = opts.tags.split(',')
|
||||
if opts.comment:
|
||||
mi.comments = opts.comment
|
||||
|
||||
|
||||
set_metadata(stream, mi)
|
||||
|
||||
print unicode(mi)
|
||||
return 0
|
||||
|
||||
|
@ -311,11 +311,14 @@ class BooksModel(QAbstractTableModel):
|
||||
ans = []
|
||||
for row in (row.row() for row in rows):
|
||||
format = None
|
||||
for f in self.db.formats(row).split(','):
|
||||
if f.lower() in formats:
|
||||
db_formats = set(self.db.formats(row).lower().split(','))
|
||||
available_formats = set([f.lower() for f in formats])
|
||||
u = available_formats.intersection(db_formats)
|
||||
for f in formats:
|
||||
if f.lower() in u:
|
||||
format = f
|
||||
break
|
||||
if format:
|
||||
if format is not None:
|
||||
pt = PersistentTemporaryFile(suffix='.'+format)
|
||||
pt.write(self.db.format(row, format))
|
||||
pt.flush()
|
||||
|
@ -1,3 +1,4 @@
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
"""
|
||||
@ -61,4 +62,17 @@ def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
|
||||
tdir = tempfile.mkdtemp(suffix, __appname__+"_"+ __version__+"_" +prefix, dir)
|
||||
atexit.register(shutil.rmtree, tdir, True)
|
||||
return tdir
|
||||
|
||||
class TemporaryDirectory(str):
|
||||
def __init__(self, suffix='', prefix='', dir=None):
|
||||
self.suffix = suffix
|
||||
self.prefix = prefix
|
||||
self.dir = dir
|
||||
|
||||
def __enter__(self):
|
||||
self.tdir = tempfile.mkdtemp(self.suffix, __appname__+"_"+ __version__+"_" +self.prefix, self.dir)
|
||||
return self.tdir
|
||||
|
||||
def __exit__(self, *args):
|
||||
shutil.rmtree(self.tdir)
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
Read and write ZIP files. Modified by Kovid Goyal to support replacing files in
|
||||
a zip archive.
|
||||
"""
|
||||
from __future__ import with_statement
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
import struct, os, time, sys, shutil
|
||||
import binascii, cStringIO
|
||||
|
||||
@ -653,10 +655,10 @@ class ZipFile:
|
||||
|
||||
fp = None # Set here since __del__ checks it
|
||||
|
||||
def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
|
||||
def __init__(self, file, mode="r", compression=ZIP_DEFLATED, allowZip64=False):
|
||||
"""Open the ZIP file with mode read "r", write "w" or append "a"."""
|
||||
if mode not in ("r", "w", "a"):
|
||||
raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
|
||||
raise RuntimeError('ZipFile() requires mode "r", "w", or "a" not %s'%mode)
|
||||
|
||||
if compression == ZIP_STORED:
|
||||
pass
|
||||
@ -856,7 +858,8 @@ class ZipFile:
|
||||
if self.filelist[j].header_offset > deleted_offset:
|
||||
self.filelist[j].header_offset -= deleted_size
|
||||
if self.filelist[j].file_offset > deleted_offset:
|
||||
self.filelist[j].file_offset -= deleted_size
|
||||
self.filelist[j].file_offset -= deleted_size
|
||||
self._didModify = True
|
||||
return
|
||||
if self.debug:
|
||||
print name, "not in archive"
|
||||
@ -1178,6 +1181,9 @@ class ZipFile:
|
||||
self.NameToInfo[zinfo.filename] = zinfo
|
||||
|
||||
def add_dir(self, path, prefix=''):
|
||||
'''
|
||||
Add a directory recursively to the zip file with an optional prefix.
|
||||
'''
|
||||
if prefix:
|
||||
self.writestr(prefix+'/', '', 0700)
|
||||
cwd = os.path.abspath(os.getcwd())
|
||||
@ -1303,6 +1309,32 @@ class ZipFile:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
|
||||
def safe_replace(zipstream, name, datastream):
|
||||
'''
|
||||
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
||||
and re-creating the zipfile. This is neccessary because :method:`ZipFile.replace`
|
||||
sometimes created corrupted zip files.
|
||||
|
||||
: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.
|
||||
'''
|
||||
z = ZipFile(zipstream, 'r')
|
||||
names = z.namelist()
|
||||
with TemporaryDirectory('_zipfile_replace') as tdir:
|
||||
z.extractall(path=tdir)
|
||||
zipstream.seek(0)
|
||||
zipstream.truncate()
|
||||
z = ZipFile(zipstream, 'w')
|
||||
path = os.path.join(tdir, *name.split('/'))
|
||||
shutil.copyfileobj(datastream, open(path, 'wb'))
|
||||
for name in names:
|
||||
current = os.path.join(tdir, *name.split('/'))
|
||||
if os.path.isdir(current):
|
||||
z.writestr(name+'/', '', 0700)
|
||||
else:
|
||||
z.write(current, name)
|
||||
z.close()
|
||||
|
||||
class PyZipFile(ZipFile):
|
||||
"""Class to create ZIP archives with Python library files and packages."""
|
||||
|
Loading…
x
Reference in New Issue
Block a user