mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	GwR revisions supporting Save to disk for .mbp, .tan
This commit is contained in:
		
						commit
						2c7da9966c
					
				@ -12,6 +12,7 @@ from cStringIO import StringIO
 | 
				
			|||||||
from struct import unpack
 | 
					from struct import unpack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from calibre.devices.usbms.driver import USBMS
 | 
					from calibre.devices.usbms.driver import USBMS
 | 
				
			||||||
 | 
					from calibre.ebooks.metadata.topaz import get_metadata as get_topaz_metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class KINDLE(USBMS):
 | 
					class KINDLE(USBMS):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -159,11 +160,12 @@ class Bookmark():
 | 
				
			|||||||
        self.id = id
 | 
					        self.id = id
 | 
				
			||||||
        self.last_read = 0
 | 
					        self.last_read = 0
 | 
				
			||||||
        self.last_read_location = 0
 | 
					        self.last_read_location = 0
 | 
				
			||||||
 | 
					        self.path = path
 | 
				
			||||||
        self.timestamp = 0
 | 
					        self.timestamp = 0
 | 
				
			||||||
        self.user_notes = None
 | 
					        self.user_notes = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.get_bookmark_data(path)
 | 
					        self.get_bookmark_data()
 | 
				
			||||||
        self.get_book_length(path)
 | 
					        self.get_book_length()
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.percent_read = float(100*self.last_read / self.book_length)
 | 
					            self.percent_read = float(100*self.last_read / self.book_length)
 | 
				
			||||||
        except:
 | 
					        except:
 | 
				
			||||||
@ -180,13 +182,13 @@ class Bookmark():
 | 
				
			|||||||
            stop, = unpack('>I', self.data[offoff + 8:offoff + 12])
 | 
					            stop, = unpack('>I', self.data[offoff + 8:offoff + 12])
 | 
				
			||||||
        return StreamSlicer(self.stream, start, stop)
 | 
					        return StreamSlicer(self.stream, start, stop)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_bookmark_data(self, path):
 | 
					    def get_bookmark_data(self):
 | 
				
			||||||
        ''' Return the timestamp and last_read_location '''
 | 
					        ''' Return the timestamp and last_read_location '''
 | 
				
			||||||
        from calibre.ebooks.metadata.mobi import StreamSlicer
 | 
					        from calibre.ebooks.metadata.mobi import StreamSlicer
 | 
				
			||||||
        user_notes = {}
 | 
					        user_notes = {}
 | 
				
			||||||
        if self.bookmark_extension == 'mbp':
 | 
					        if self.bookmark_extension == 'mbp':
 | 
				
			||||||
            MAGIC_MOBI_CONSTANT = 150
 | 
					            MAGIC_MOBI_CONSTANT = 150
 | 
				
			||||||
            with open(path,'rb') as f:
 | 
					            with open(self.path,'rb') as f:
 | 
				
			||||||
                stream = StringIO(f.read())
 | 
					                stream = StringIO(f.read())
 | 
				
			||||||
                data = StreamSlicer(stream)
 | 
					                data = StreamSlicer(stream)
 | 
				
			||||||
                self.timestamp, = unpack('>I', data[0x24:0x28])
 | 
					                self.timestamp, = unpack('>I', data[0x24:0x28])
 | 
				
			||||||
@ -204,7 +206,7 @@ class Bookmark():
 | 
				
			|||||||
                eo = bpar_offset + bpar_len
 | 
					                eo = bpar_offset + bpar_len
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Walk bookmark entries
 | 
					                # Walk bookmark entries
 | 
				
			||||||
                #print " --- %s --- " % path
 | 
					                #print " --- %s --- " % self.path
 | 
				
			||||||
                current_entry = 1
 | 
					                current_entry = 1
 | 
				
			||||||
                sig = data[eo:eo+4]
 | 
					                sig = data[eo:eo+4]
 | 
				
			||||||
                previous_block = None
 | 
					                previous_block = None
 | 
				
			||||||
@ -243,18 +245,28 @@ class Bookmark():
 | 
				
			|||||||
                while sig == 'BKMK':
 | 
					                while sig == 'BKMK':
 | 
				
			||||||
                    # Fix start location for Highlights using BKMK data
 | 
					                    # Fix start location for Highlights using BKMK data
 | 
				
			||||||
                    end_loc, = unpack('>I', data[eo+0x10:eo+0x14])
 | 
					                    end_loc, = unpack('>I', data[eo+0x10:eo+0x14])
 | 
				
			||||||
                    if end_loc in user_notes and user_notes[end_loc]['type'] == 'Highlight':
 | 
					
 | 
				
			||||||
 | 
					                    if end_loc in user_notes and \
 | 
				
			||||||
 | 
					                       (user_notes[end_loc]['type'] == 'Highlight' or \
 | 
				
			||||||
 | 
					                        user_notes[end_loc]['type'] == 'Note'):
 | 
				
			||||||
 | 
					                        # Switch location to start (0x08:0x0c)
 | 
				
			||||||
                        start, = unpack('>I', data[eo+8:eo+12])
 | 
					                        start, = unpack('>I', data[eo+8:eo+12])
 | 
				
			||||||
                        user_notes[start] = user_notes[end_loc]
 | 
					                        user_notes[start] = user_notes[end_loc]
 | 
				
			||||||
 | 
					                        '''
 | 
				
			||||||
 | 
					                        print " %s: swapping 0x%x (%d) to 0x%x (%d)" % (user_notes[end_loc]['type'],
 | 
				
			||||||
 | 
					                                                                    end_loc,
 | 
				
			||||||
 | 
					                                                                    end_loc/MAGIC_MOBI_CONSTANT + 1,
 | 
				
			||||||
 | 
					                                                                    start,
 | 
				
			||||||
 | 
					                                                                    start//MAGIC_MOBI_CONSTANT + 1)
 | 
				
			||||||
 | 
					                        '''
 | 
				
			||||||
 | 
					                        user_notes[start]['displayed_location'] = start/MAGIC_MOBI_CONSTANT + 1
 | 
				
			||||||
                        user_notes.pop(end_loc)
 | 
					                        user_notes.pop(end_loc)
 | 
				
			||||||
                    elif end_loc in user_notes and user_notes[end_loc]['type'] == 'Note':
 | 
					 | 
				
			||||||
                        # Skip duplicate bookmarks for notes
 | 
					 | 
				
			||||||
                        pass
 | 
					 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        # If a bookmark coincides with a user annotation, the locs could
 | 
					                        # If a bookmark coincides with a user annotation, the locs could
 | 
				
			||||||
                        # be the same - cheat by nudging -1
 | 
					                        # be the same - cheat by nudging -1
 | 
				
			||||||
                        # Skip bookmark for last_read_location
 | 
					                        # Skip bookmark for last_read_location
 | 
				
			||||||
                        if end_loc != self.last_read:
 | 
					                        if end_loc != self.last_read:
 | 
				
			||||||
 | 
					                            # print " adding Bookmark at 0x%x (%d)" % (end_loc, end_loc/MAGIC_MOBI_CONSTANT + 1)
 | 
				
			||||||
                            displayed_location = end_loc/MAGIC_MOBI_CONSTANT + 1
 | 
					                            displayed_location = end_loc/MAGIC_MOBI_CONSTANT + 1
 | 
				
			||||||
                            user_notes[end_loc - 1] = dict(id=self.id,
 | 
					                            user_notes[end_loc - 1] = dict(id=self.id,
 | 
				
			||||||
                                                           displayed_location=displayed_location,
 | 
					                                                           displayed_location=displayed_location,
 | 
				
			||||||
@ -265,10 +277,41 @@ class Bookmark():
 | 
				
			|||||||
                    sig = data[eo:eo+4]
 | 
					                    sig = data[eo:eo+4]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        elif self.bookmark_extension == 'tan':
 | 
					        elif self.bookmark_extension == 'tan':
 | 
				
			||||||
            # TAN bookmarks
 | 
					            def get_topaz_highlight(displayed_location):
 | 
				
			||||||
 | 
					                # Parse My Clippings.txt for a matching highlight
 | 
				
			||||||
 | 
					                book_fs = self.path.replace('.%s' % self.bookmark_extension,'.%s' % self.book_format)
 | 
				
			||||||
 | 
					                with open(book_fs,'rb') as f2:
 | 
				
			||||||
 | 
					                    stream = StringIO(f2.read())
 | 
				
			||||||
 | 
					                    mi = get_topaz_metadata(stream)
 | 
				
			||||||
 | 
					                my_clippings = self.path
 | 
				
			||||||
 | 
					                split = my_clippings.find('documents') + len('documents/')
 | 
				
			||||||
 | 
					                my_clippings = my_clippings[:split] + "My Clippings.txt"
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    with open(my_clippings, 'r') as f2:
 | 
				
			||||||
 | 
					                        marker_found = 0
 | 
				
			||||||
 | 
					                        text = ''
 | 
				
			||||||
 | 
					                        search_str1 = '%s (%s)' % (mi.title, str(mi.author[0]))
 | 
				
			||||||
 | 
					                        search_str2 = '- Highlight Loc. %d' % (displayed_location)
 | 
				
			||||||
 | 
					                        for line in f2:
 | 
				
			||||||
 | 
					                            if marker_found == 0:
 | 
				
			||||||
 | 
					                                if line.startswith(search_str1):
 | 
				
			||||||
 | 
					                                    marker_found = 1
 | 
				
			||||||
 | 
					                            elif marker_found == 1:
 | 
				
			||||||
 | 
					                                if line.startswith(search_str2):
 | 
				
			||||||
 | 
					                                    marker_found = 2
 | 
				
			||||||
 | 
					                            elif marker_found == 2:
 | 
				
			||||||
 | 
					                                if line.startswith('=========='):
 | 
				
			||||||
 | 
					                                    break
 | 
				
			||||||
 | 
					                                text += line.strip()
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            raise error
 | 
				
			||||||
 | 
					                except:
 | 
				
			||||||
 | 
					                    text = '(Unable to extract highlight text from My Clippings.txt)'
 | 
				
			||||||
 | 
					                return text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            MAGIC_TOPAZ_CONSTANT = 33.33
 | 
					            MAGIC_TOPAZ_CONSTANT = 33.33
 | 
				
			||||||
            self.timestamp = os.path.getmtime(path)
 | 
					            self.timestamp = os.path.getmtime(self.path)
 | 
				
			||||||
            with open(path,'rb') as f:
 | 
					            with open(self.path,'rb') as f:
 | 
				
			||||||
                stream = StringIO(f.read())
 | 
					                stream = StringIO(f.read())
 | 
				
			||||||
                data = StreamSlicer(stream)
 | 
					                data = StreamSlicer(stream)
 | 
				
			||||||
                self.last_read = int(unpack('>I', data[5:9])[0])
 | 
					                self.last_read = int(unpack('>I', data[5:9])[0])
 | 
				
			||||||
@ -285,7 +328,7 @@ class Bookmark():
 | 
				
			|||||||
                        e_type = 'Bookmark'
 | 
					                        e_type = 'Bookmark'
 | 
				
			||||||
                    elif e_type == 1:
 | 
					                    elif e_type == 1:
 | 
				
			||||||
                        e_type = 'Highlight'
 | 
					                        e_type = 'Highlight'
 | 
				
			||||||
                        text = "(Topaz highlights not yet supported)"
 | 
					                        text = get_topaz_highlight(location/MAGIC_TOPAZ_CONSTANT + 1)
 | 
				
			||||||
                    elif e_type == 2:
 | 
					                    elif e_type == 2:
 | 
				
			||||||
                        e_type = 'Note'
 | 
					                        e_type = 'Note'
 | 
				
			||||||
                        text = data[e_base+0x10:e_base+0x10+text_len]
 | 
					                        text = data[e_base+0x10:e_base+0x10+text_len]
 | 
				
			||||||
@ -293,10 +336,9 @@ class Bookmark():
 | 
				
			|||||||
                        e_type = 'Unknown annotation type'
 | 
					                        e_type = 'Unknown annotation type'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if self.book_format in ['tpz','azw1']:
 | 
					                    if self.book_format in ['tpz','azw1']:
 | 
				
			||||||
                        # *** This needs fine-tuning
 | 
					 | 
				
			||||||
                        displayed_location = location/MAGIC_TOPAZ_CONSTANT + 1
 | 
					                        displayed_location = location/MAGIC_TOPAZ_CONSTANT + 1
 | 
				
			||||||
                    elif self.book_format == 'pdf':
 | 
					                    elif self.book_format == 'pdf':
 | 
				
			||||||
                        # *** This needs testing
 | 
					                        # *** This needs implementation
 | 
				
			||||||
                        displayed_location = location
 | 
					                        displayed_location = location
 | 
				
			||||||
                    user_notes[location] = dict(id=self.id,
 | 
					                    user_notes[location] = dict(id=self.id,
 | 
				
			||||||
                                                displayed_location=displayed_location,
 | 
					                                                displayed_location=displayed_location,
 | 
				
			||||||
@ -315,16 +357,9 @@ class Bookmark():
 | 
				
			|||||||
            print "unsupported bookmark_extension: %s" % self.bookmark_extension
 | 
					            print "unsupported bookmark_extension: %s" % self.bookmark_extension
 | 
				
			||||||
        self.user_notes = user_notes
 | 
					        self.user_notes = user_notes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        '''
 | 
					    def get_book_length(self):
 | 
				
			||||||
        for location in sorted(user_notes):
 | 
					 | 
				
			||||||
            print '  Location %d: %s\n%s' % (user_notes[location]['displayed_location'],
 | 
					 | 
				
			||||||
                                                     user_notes[location]['type'],
 | 
					 | 
				
			||||||
                                    '\n'.join(self.textdump(user_notes[location]['text'])))
 | 
					 | 
				
			||||||
        '''
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_book_length(self, path):
 | 
					 | 
				
			||||||
        from calibre.ebooks.metadata.mobi import StreamSlicer
 | 
					        from calibre.ebooks.metadata.mobi import StreamSlicer
 | 
				
			||||||
        book_fs = path.replace('.%s' % self.bookmark_extension,'.%s' % self.book_format)
 | 
					        book_fs = self.path.replace('.%s' % self.bookmark_extension,'.%s' % self.book_format)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.book_length = 0
 | 
					        self.book_length = 0
 | 
				
			||||||
        if self.bookmark_extension == 'mbp':
 | 
					        if self.bookmark_extension == 'mbp':
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ class DRMError(ValueError):
 | 
				
			|||||||
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
 | 
					BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
 | 
				
			||||||
                   'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc',
 | 
					                   'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc',
 | 
				
			||||||
                   'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
 | 
					                   'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip',
 | 
				
			||||||
                   'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml']
 | 
					                   'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'mbp', 'tan']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HTMLRenderer(object):
 | 
					class HTMLRenderer(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -336,6 +336,13 @@ class MetadataUpdater(object):
 | 
				
			|||||||
        if mi.publisher:
 | 
					        if mi.publisher:
 | 
				
			||||||
            update_exth_record((101, mi.publisher.encode(self.codec, 'replace')))
 | 
					            update_exth_record((101, mi.publisher.encode(self.codec, 'replace')))
 | 
				
			||||||
        if mi.comments:
 | 
					        if mi.comments:
 | 
				
			||||||
 | 
					            # Strip user annotations
 | 
				
			||||||
 | 
					            a_offset = mi.comments.find('<div class="user_annotations">')
 | 
				
			||||||
 | 
					            ad_offset = mi.comments.find('<hr class="annotations_divider" />')
 | 
				
			||||||
 | 
					            if a_offset >= 0:
 | 
				
			||||||
 | 
					                mi.comments = mi.comments[:a_offset]
 | 
				
			||||||
 | 
					            if ad_offset >= 0:
 | 
				
			||||||
 | 
					                mi.comments = mi.comments[:ad_offset]
 | 
				
			||||||
            update_exth_record((103, mi.comments.encode(self.codec, 'replace')))
 | 
					            update_exth_record((103, mi.comments.encode(self.codec, 'replace')))
 | 
				
			||||||
        if mi.isbn:
 | 
					        if mi.isbn:
 | 
				
			||||||
            update_exth_record((104, mi.isbn.encode(self.codec, 'replace')))
 | 
					            update_exth_record((104, mi.isbn.encode(self.codec, 'replace')))
 | 
				
			||||||
 | 
				
			|||||||
@ -1074,6 +1074,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
 | 
				
			|||||||
                        mi.comments = unicode(user_notes_soup.prettify())
 | 
					                        mi.comments = unicode(user_notes_soup.prettify())
 | 
				
			||||||
                    # Update library comments
 | 
					                    # Update library comments
 | 
				
			||||||
                    self.db.set_comment(id, mi.comments)
 | 
					                    self.db.set_comment(id, mi.comments)
 | 
				
			||||||
 | 
					                    '''
 | 
				
			||||||
 | 
					                    # Add bookmark file to id
 | 
				
			||||||
 | 
					                    self.db.add_format_with_hooks(id, bm.bookmark.bookmark_extension,
 | 
				
			||||||
 | 
					                                                  bm.bookmark.path, index_is_id=True)
 | 
				
			||||||
 | 
					                    '''
 | 
				
			||||||
                    self.update_progress.emit(i)
 | 
					                    self.update_progress.emit(i)
 | 
				
			||||||
                self.update_done.emit()
 | 
					                self.update_done.emit()
 | 
				
			||||||
                self.done_callback(self.am.keys())
 | 
					                self.done_callback(self.am.keys())
 | 
				
			||||||
@ -1516,6 +1521,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI):
 | 
				
			|||||||
            opts = config().parse()
 | 
					            opts = config().parse()
 | 
				
			||||||
            if single_format is not None:
 | 
					            if single_format is not None:
 | 
				
			||||||
                opts.formats = single_format
 | 
					                opts.formats = single_format
 | 
				
			||||||
 | 
					                # Special case for Kindle annotation files
 | 
				
			||||||
 | 
					                if single_format.lower() == 'mbp' or single_format == 'tan':
 | 
				
			||||||
 | 
					                    opts.to_lowercase = False
 | 
				
			||||||
 | 
					                    opts.save_cover = False
 | 
				
			||||||
 | 
					                    opts.write_opf = False
 | 
				
			||||||
 | 
					                    opts.template = opts.send_template
 | 
				
			||||||
            if single_dir:
 | 
					            if single_dir:
 | 
				
			||||||
                opts.template = opts.template.split('/')[-1].strip()
 | 
					                opts.template = opts.template.split('/')[-1].strip()
 | 
				
			||||||
                if not opts.template:
 | 
					                if not opts.template:
 | 
				
			||||||
 | 
				
			|||||||
@ -1052,7 +1052,16 @@ class EPUB_MOBI(CatalogPlugin):
 | 
				
			|||||||
                this_title['rating'] = record['rating'] if record['rating'] else 0
 | 
					                this_title['rating'] = record['rating'] if record['rating'] else 0
 | 
				
			||||||
                this_title['date'] = strftime(u'%B %Y', record['pubdate'].timetuple())
 | 
					                this_title['date'] = strftime(u'%B %Y', record['pubdate'].timetuple())
 | 
				
			||||||
                this_title['timestamp'] = record['timestamp']
 | 
					                this_title['timestamp'] = record['timestamp']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if record['comments']:
 | 
					                if record['comments']:
 | 
				
			||||||
 | 
					                    # Strip annotations
 | 
				
			||||||
 | 
					                    a_offset = record['comments'].find('<div class="user_annotations">')
 | 
				
			||||||
 | 
					                    ad_offset = record['comments'].find('<hr class="annotations_divider" />')
 | 
				
			||||||
 | 
					                    if a_offset >= 0:
 | 
				
			||||||
 | 
					                        record['comments'] = record['comments'][:a_offset]
 | 
				
			||||||
 | 
					                    if ad_offset >= 0:
 | 
				
			||||||
 | 
					                        record['comments'] = record['comments'][:ad_offset]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    this_title['description'] = self.markdownComments(record['comments'])
 | 
					                    this_title['description'] = self.markdownComments(record['comments'])
 | 
				
			||||||
                    paras = BeautifulSoup(this_title['description']).findAll('p')
 | 
					                    paras = BeautifulSoup(this_title['description']).findAll('p')
 | 
				
			||||||
                    tokens = []
 | 
					                    tokens = []
 | 
				
			||||||
 | 
				
			|||||||
@ -125,7 +125,7 @@ With recent reader iterations, SONY, in all its wisdom has decided to try to for
 | 
				
			|||||||
use their software. If you install it, it auto-launches whenever you connect the reader.
 | 
					use their software. If you install it, it auto-launches whenever you connect the reader.
 | 
				
			||||||
If you don't want to uninstall it altogether, there are a couple of tricks you can use. The
 | 
					If you don't want to uninstall it altogether, there are a couple of tricks you can use. The
 | 
				
			||||||
simplest is to simply re-name the executable file that launches the library program. More detail
 | 
					simplest is to simply re-name the executable file that launches the library program. More detail
 | 
				
			||||||
`here http://www.mobileread.com/forums/showthread.php?t=65809`_.
 | 
					`here <http://www.mobileread.com/forums/showthread.php?t=65809>`_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Can I use the collections feature of the SONY reader?
 | 
					Can I use the collections feature of the SONY reader?
 | 
				
			||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
					~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
				
			||||||
 | 
				
			|||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user