home *** CD-ROM | disk | FTP | other *** search
- # Source Generated with Decompyle++
- # File: in.pyc (Python 2.6)
-
- from __future__ import with_statement
- __license__ = 'GPL 3'
- __copyright__ = '2010, Greg Riker <griker@hotmail.com>'
- __docformat__ = 'restructuredtext en'
- import StringIO
- import sys
- from struct import pack
- from calibre.ebooks.metadata import MetaInformation
-
- class StreamSlicer(object):
-
- def __init__(self, stream, start = 0, stop = None):
- self._stream = stream
- self.start = start
- if stop is None:
- stream.seek(0, 2)
- stop = stream.tell()
-
- self.stop = stop
- self._len = stop - start
-
-
- def __len__(self):
- return self._len
-
-
- def __getitem__(self, key):
- stream = self._stream
- base = self.start
- if isinstance(key, (int, long)):
- stream.seek(base + key)
- return stream.read(1)
- if isinstance(key, slice):
- (start, stop, stride) = key.indices(self._len)
- if stride < 0:
- start = stop
- stop = start
-
- size = stop - start
- if size <= 0:
- return ''
- stream.seek(base + start)
- data = stream.read(size)
- if stride != 1:
- data = data[::stride]
-
- return data
- raise TypeError('stream indices must be integers')
-
-
- def __setitem__(self, key, value):
- stream = self._stream
- base = self.start
- if isinstance(key, (int, long)):
- if len(value) != 1:
- raise ValueError('key and value lengths must match')
- len(value) != 1
- stream.seek(base + key)
- return stream.write(value)
- if isinstance(key, slice):
- (start, stop, stride) = key.indices(self._len)
- if stride < 0:
- start = stop
- stop = start
-
- size = stop - start
- if stride != 1:
- value = value[::stride]
-
- if len(value) != size:
- raise ValueError('key and value lengths must match')
- len(value) != size
- stream.seek(base + start)
- return stream.write(value)
- raise TypeError('stream indices must be integers')
-
-
- def update(self, data_blocks):
- stream = self._stream
- base = self.start
- stream.seek(base)
- self._stream.truncate(base)
- for block in data_blocks:
- stream.write(block)
-
-
-
- def truncate(self, value):
- self._stream.truncate(value)
-
-
-
- class MetadataUpdater(object):
-
- def __init__(self, stream):
- self.stream = stream
- self.data = StreamSlicer(stream)
- sig = self.data[:4]
- if not sig.startswith('TPZ'):
- raise ValueError("'%s': Not a Topaz file" % getattr(stream, 'name', 'Unnamed stream'))
- sig.startswith('TPZ')
- offset = 4
- (self.header_records, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- (self.topaz_headers, self.th_seq) = self.get_headers(offset)
- if 'metadata' not in self.topaz_headers:
- raise ValueError("'%s': Invalid Topaz format - no metadata record" % getattr(stream, 'name', 'Unnamed stream'))
- 'metadata' not in self.topaz_headers
- md_offset = self.topaz_headers['metadata']['blocks'][0]['offset']
- md_offset += self.base
- if self.data[md_offset + 1:md_offset + 9] != 'metadata':
- raise ValueError("'%s': Damaged metadata record" % getattr(stream, 'name', 'Unnamed stream'))
- self.data[md_offset + 1:md_offset + 9] != 'metadata'
-
-
- def book_length(self):
- self.get_original_metadata()
- if 'bookLength' in self.metadata:
- return int(self.metadata['bookLength'])
- return 0
-
-
- def decode_vwi(self, bytes):
- (pos, val) = (0, 0)
- done = False
- while pos < len(bytes) and not done:
- b = ord(bytes[pos])
- pos += 1
- if b & 128 == 0:
- done = True
-
- b &= 127
- val <<= 7
- val |= b
- if done:
- break
- continue
- return (val, pos)
-
-
- def dump_headers(self):
- print '\ndump_headers():'
- for tag in self.topaz_headers:
- print '%s: ' % tag
- num_recs = len(self.topaz_headers[tag]['blocks'])
- print ' num_recs: %d' % num_recs
- if num_recs:
- print ' starting offset: 0x%x' % self.topaz_headers[tag]['blocks'][0]['offset']
- continue
-
-
-
- def dump_hex(self, src, length = 16):
- FILTER = []([ '.' for x in range(256) ])
- N = 0
- result = ''
- for x in s:
- hexa = _[2](_[2]['%02X' % ord(x)])
- s = s.translate(FILTER)
- result += '%04X %-*s %s\n' % (N, length * 3, hexa, s)
- N += length
- []
- print result
-
-
- def dump_metadata(self):
- for tag in self.metadata:
- print '%s: %s' % (tag, repr(self.metadata[tag]))
-
-
-
- def encode_vwi(self, value):
- bytes = []
- multi_byte = value > 127
- while value:
- b = value & 127
- value >>= 7
- if value == 0:
- if multi_byte:
- bytes.append(b | 128)
- if bytes[-1] == 255:
- bytes.append(128)
-
- if len(bytes) == 4:
- return pack('>BBBB', bytes[3], bytes[2], bytes[1], bytes[0]).decode('iso-8859-1')
- if len(bytes) == 3:
- return pack('>BBB', bytes[2], bytes[1], bytes[0]).decode('iso-8859-1')
- if len(bytes) == 2:
- return pack('>BB', bytes[1], bytes[0]).decode('iso-8859-1')
- else:
- return pack('>B', b).decode('iso-8859-1')
- multi_byte
- if len(bytes):
- bytes.append(b | 128)
- continue
- bytes.append(b)
- return pack('>B', 0).decode('iso-8859-1')
-
-
- def generate_dkey(self):
- for x in self.topaz_headers:
- if self.topaz_headers[x]['tag'] == 'dkey':
- if self.topaz_headers[x]['blocks']:
- offset = self.base + self.topaz_headers[x]['blocks'][0]['offset']
- len_uncomp = self.topaz_headers[x]['blocks'][0]['len_uncomp']
- break
- else:
- return None
- self.topaz_headers[x]['blocks']
-
- dkey = self.topaz_headers[x]
- dks = StringIO.StringIO()
- dks.write(self.encode_vwi(len(dkey['tag'])))
- offset += 1
- dks.write(dkey['tag'])
- offset += len('dkey')
- dks.write(chr(0))
- offset += 1
- dks.write(self.data[offset:offset + len_uncomp].decode('iso-8859-1'))
- return dks.getvalue().encode('iso-8859-1')
-
-
- def get_headers(self, offset):
- topaz_headers = { }
- th_seq = []
- for x in range(self.header_records):
- offset += 1
- (taglen, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- tag = self.data[offset:offset + taglen]
- offset += taglen
- (num_vals, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- blocks = { }
- for val in range(num_vals):
- (hdr_offset, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- (len_uncomp, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- (len_comp, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- blocks[val] = dict(offset = hdr_offset, len_uncomp = len_uncomp, len_comp = len_comp)
-
- topaz_headers[tag] = dict(blocks = blocks)
- th_seq.append(tag)
-
- self.eoth = self.data[offset]
- offset += 1
- self.base = offset
- return (topaz_headers, th_seq)
-
-
- def generate_metadata_stream(self):
- ms = StringIO.StringIO()
- ms.write(self.encode_vwi(len(self.md_header['tag'])).encode('iso-8859-1'))
- ms.write(self.md_header['tag'])
- ms.write(chr(self.md_header['flags']))
- ms.write(chr(len(self.metadata)))
- for tag in self.md_seq:
- ms.write(self.encode_vwi(len(tag)).encode('iso-8859-1'))
- ms.write(tag)
- ms.write(self.encode_vwi(len(self.metadata[tag])).encode('iso-8859-1'))
- ms.write(self.metadata[tag])
-
- return ms.getvalue()
-
-
- def get_metadata(self):
- self.get_original_metadata()
- return MetaInformation(self.metadata['Title'], [
- self.metadata['Authors']])
-
-
- def get_original_metadata(self):
- offset = self.base + self.topaz_headers['metadata']['blocks'][0]['offset']
- self.md_header = { }
- (taglen, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- self.md_header['tag'] = self.data[offset:offset + taglen]
- offset += taglen
- self.md_header['flags'] = ord(self.data[offset])
- offset += 1
- self.md_header['num_recs'] = ord(self.data[offset])
- offset += 1
- self.metadata = { }
- self.md_seq = []
- for x in range(self.md_header['num_recs']):
- (taglen, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- tag = self.data[offset:offset + taglen]
- offset += taglen
- (md_len, consumed) = self.decode_vwi(self.data[offset:offset + 4])
- offset += consumed
- metadata = self.data[offset:offset + md_len]
- offset += md_len
- self.metadata[tag] = metadata
- self.md_seq.append(tag)
-
-
-
- def regenerate_headers(self, updated_md_len):
- original_md_len = self.topaz_headers['metadata']['blocks'][0]['len_uncomp']
- original_md_offset = self.topaz_headers['metadata']['blocks'][0]['offset']
- delta = updated_md_len - original_md_len
- ths = StringIO.StringIO()
- ths.write(self.data[:5])
- for tag in self.th_seq:
- ths.write('c')
- ths.write(self.encode_vwi(len(tag)))
- ths.write(tag)
- if self.topaz_headers[tag]['blocks']:
- ths.write(self.encode_vwi(len(self.topaz_headers[tag]['blocks'])))
- for block in self.topaz_headers[tag]['blocks']:
- b = self.topaz_headers[tag]['blocks'][block]
- if b['offset'] <= original_md_offset:
- ths.write(self.encode_vwi(b['offset']))
- else:
- ths.write(self.encode_vwi(b['offset'] + delta))
- if tag == 'metadata':
- ths.write(self.encode_vwi(updated_md_len))
- else:
- ths.write(self.encode_vwi(b['len_uncomp']))
- ths.write(self.encode_vwi(b['len_comp']))
-
- ths.write(self.encode_vwi(0))
-
- self.original_md_start = original_md_offset + self.base
- self.original_md_len = original_md_len
- return ths.getvalue().encode('iso-8859-1')
-
-
- def update(self, mi):
- self.get_original_metadata()
-
- try:
- load_defaults = load_defaults
- import calibre.ebooks.conversion.config
- prefs = load_defaults('mobi_output')
- pas = prefs.get('prefer_author_sort', False)
- except:
- pas = False
-
- if mi.author_sort and pas:
- authors = mi.author_sort
- self.metadata['Authors'] = authors.encode('utf-8')
- elif mi.authors:
- authors = '; '.join(mi.authors)
- self.metadata['Authors'] = authors.encode('utf-8')
-
- self.metadata['Title'] = mi.title.encode('utf-8')
- updated_metadata = self.generate_metadata_stream()
- prefix = len('metadata') + 2
- um_buf_len = len(updated_metadata) - prefix
- head = self.regenerate_headers(um_buf_len)
- chunk1 = self.data[self.base:self.original_md_start]
- chunk2 = self.data[prefix + self.original_md_start + self.original_md_len:]
- self.stream.seek(0)
- self.stream.truncate(0)
- self.stream.write(head)
- self.stream.write('d')
- self.stream.write(chunk1)
- self.stream.write(updated_metadata)
- self.stream.write(chunk2)
-
-
-
- def get_metadata(stream):
- mu = MetadataUpdater(stream)
- return mu.get_metadata()
-
-
- def set_metadata(stream, mi):
- mu = MetadataUpdater(stream)
- mu.update(mi)
-
- if __name__ == '__main__':
- if False:
- print get_metadata(open(sys.argv[1], 'rb'))
- else:
- import cStringIO
- data = open(sys.argv[1], 'rb')
- stream = cStringIO.StringIO()
- stream.write(data.read())
- mi = MetaInformation(title = 'Updated Title', authors = [
- 'Author, Random'])
- set_metadata(stream, mi)
- tokens = sys.argv[1].rpartition('.')
- updated_data = open(tokens[0] + '-updated' + '.' + tokens[2], 'wb')
- updated_data.write(stream.getvalue())
- updated_data.close()
-
-