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_ID = 0x031e #: Product Id for the PRS-505
|
||||||
PRODUCT_NAME = 'PRS-505'
|
PRODUCT_NAME = 'PRS-505'
|
||||||
VENDOR_NAME = 'SONY'
|
VENDOR_NAME = 'SONY'
|
||||||
FORMATS = ["lrf", 'epub', "rtf", "pdf", "txt"]
|
FORMATS = ['lrf', 'epub', "rtf", "pdf", "txt"]
|
||||||
|
|
||||||
MEDIA_XML = 'database/cache/media.xml'
|
MEDIA_XML = 'database/cache/media.xml'
|
||||||
CACHE_XML = 'Sony Reader/database/cache.xml'
|
CACHE_XML = 'Sony Reader/database/cache.xml'
|
||||||
|
@ -22,6 +22,7 @@ __docformat__ = "epytext"
|
|||||||
preferred_source_formats = [
|
preferred_source_formats = [
|
||||||
'LIT',
|
'LIT',
|
||||||
'MOBI',
|
'MOBI',
|
||||||
|
'EPUB',
|
||||||
'HTML',
|
'HTML',
|
||||||
'HTM',
|
'HTM',
|
||||||
'XHTM',
|
'XHTM',
|
||||||
|
@ -7,7 +7,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
|
|
||||||
from calibre.utils.zipfile import ZipFile, BadZipfile
|
from calibre.utils.zipfile import ZipFile, BadZipfile, safe_replace
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from contextlib import closing
|
from contextlib import closing
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class OCFReader(OCF):
|
|||||||
class OCFZipReader(OCFReader):
|
class OCFZipReader(OCFReader):
|
||||||
def __init__(self, stream, mode='r'):
|
def __init__(self, stream, mode='r'):
|
||||||
try:
|
try:
|
||||||
self.archive = ZipFile(stream, mode)
|
self.archive = ZipFile(stream, mode=mode)
|
||||||
except BadZipfile:
|
except BadZipfile:
|
||||||
raise EPubException("not a ZIP .epub OCF container")
|
raise EPubException("not a ZIP .epub OCF container")
|
||||||
self.root = getattr(stream, 'name', os.getcwd())
|
self.root = getattr(stream, 'name', os.getcwd())
|
||||||
@ -82,18 +82,20 @@ class OCFZipReader(OCFReader):
|
|||||||
def open(self, name, mode='r'):
|
def open(self, name, mode='r'):
|
||||||
return StringIO(self.archive.read(name))
|
return StringIO(self.archive.read(name))
|
||||||
|
|
||||||
class OCFZipWriter(OCFZipReader):
|
class OCFZipWriter(object):
|
||||||
|
|
||||||
def __init__(self, stream):
|
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):
|
def set_metadata(self, mi):
|
||||||
name = self.container[OPF.MIMETYPE]
|
|
||||||
stream = StringIO()
|
stream = StringIO()
|
||||||
opf = OPFCreator(self.root, mi)
|
opf = OPFCreator(self.root, mi)
|
||||||
opf.render(stream)
|
opf.render(stream)
|
||||||
self.archive.delete(name)
|
stream.seek(0)
|
||||||
self.archive.writestr(name, stream.getvalue())
|
safe_replace(self.stream, self.opf, stream)
|
||||||
|
|
||||||
class OCFDirReader(OCFReader):
|
class OCFDirReader(OCFReader):
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
@ -135,7 +137,6 @@ def main(args=sys.argv):
|
|||||||
mi.comments = opts.comment
|
mi.comments = opts.comment
|
||||||
|
|
||||||
set_metadata(stream, mi)
|
set_metadata(stream, mi)
|
||||||
|
|
||||||
print unicode(mi)
|
print unicode(mi)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
@ -311,11 +311,14 @@ class BooksModel(QAbstractTableModel):
|
|||||||
ans = []
|
ans = []
|
||||||
for row in (row.row() for row in rows):
|
for row in (row.row() for row in rows):
|
||||||
format = None
|
format = None
|
||||||
for f in self.db.formats(row).split(','):
|
db_formats = set(self.db.formats(row).lower().split(','))
|
||||||
if f.lower() in formats:
|
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
|
format = f
|
||||||
break
|
break
|
||||||
if format:
|
if format is not None:
|
||||||
pt = PersistentTemporaryFile(suffix='.'+format)
|
pt = PersistentTemporaryFile(suffix='.'+format)
|
||||||
pt.write(self.db.format(row, format))
|
pt.write(self.db.format(row, format))
|
||||||
pt.flush()
|
pt.flush()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
"""
|
"""
|
||||||
@ -62,3 +63,16 @@ def PersistentTemporaryDirectory(suffix='', prefix='', dir=None):
|
|||||||
atexit.register(shutil.rmtree, tdir, True)
|
atexit.register(shutil.rmtree, tdir, True)
|
||||||
return tdir
|
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
|
Read and write ZIP files. Modified by Kovid Goyal to support replacing files in
|
||||||
a zip archive.
|
a zip archive.
|
||||||
"""
|
"""
|
||||||
|
from __future__ import with_statement
|
||||||
|
from calibre.ptempfile import TemporaryDirectory
|
||||||
import struct, os, time, sys, shutil
|
import struct, os, time, sys, shutil
|
||||||
import binascii, cStringIO
|
import binascii, cStringIO
|
||||||
|
|
||||||
@ -653,10 +655,10 @@ class ZipFile:
|
|||||||
|
|
||||||
fp = None # Set here since __del__ checks it
|
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"."""
|
"""Open the ZIP file with mode read "r", write "w" or append "a"."""
|
||||||
if mode not in ("r", "w", "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:
|
if compression == ZIP_STORED:
|
||||||
pass
|
pass
|
||||||
@ -857,6 +859,7 @@ class ZipFile:
|
|||||||
self.filelist[j].header_offset -= deleted_size
|
self.filelist[j].header_offset -= deleted_size
|
||||||
if self.filelist[j].file_offset > deleted_offset:
|
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
|
return
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print name, "not in archive"
|
print name, "not in archive"
|
||||||
@ -1178,6 +1181,9 @@ class ZipFile:
|
|||||||
self.NameToInfo[zinfo.filename] = zinfo
|
self.NameToInfo[zinfo.filename] = zinfo
|
||||||
|
|
||||||
def add_dir(self, path, prefix=''):
|
def add_dir(self, path, prefix=''):
|
||||||
|
'''
|
||||||
|
Add a directory recursively to the zip file with an optional prefix.
|
||||||
|
'''
|
||||||
if prefix:
|
if prefix:
|
||||||
self.writestr(prefix+'/', '', 0700)
|
self.writestr(prefix+'/', '', 0700)
|
||||||
cwd = os.path.abspath(os.getcwd())
|
cwd = os.path.abspath(os.getcwd())
|
||||||
@ -1303,6 +1309,32 @@ class ZipFile:
|
|||||||
self.fp.close()
|
self.fp.close()
|
||||||
self.fp = None
|
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 PyZipFile(ZipFile):
|
||||||
"""Class to create ZIP archives with Python library files and packages."""
|
"""Class to create ZIP archives with Python library files and packages."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user