diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index b1ec52d214..1482c304b4 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -159,11 +159,12 @@ class Bookmark(): self.id = id self.last_read = 0 self.last_read_location = 0 + self.path = path self.timestamp = 0 self.user_notes = None - self.get_bookmark_data(path) - self.get_book_length(path) + self.get_bookmark_data() + self.get_book_length() try: self.percent_read = float(100*self.last_read / self.book_length) except: @@ -180,13 +181,13 @@ class Bookmark(): stop, = unpack('>I', self.data[offoff + 8:offoff + 12]) return StreamSlicer(self.stream, start, stop) - def get_bookmark_data(self, path): + def get_bookmark_data(self): ''' Return the timestamp and last_read_location ''' from calibre.ebooks.metadata.mobi import StreamSlicer user_notes = {} if self.bookmark_extension == 'mbp': MAGIC_MOBI_CONSTANT = 150 - with open(path,'rb') as f: + with open(self.path,'rb') as f: stream = StringIO(f.read()) data = StreamSlicer(stream) self.timestamp, = unpack('>I', data[0x24:0x28]) @@ -204,7 +205,7 @@ class Bookmark(): eo = bpar_offset + bpar_len # Walk bookmark entries - #print " --- %s --- " % path + #print " --- %s --- " % self.path current_entry = 1 sig = data[eo:eo+4] previous_block = None @@ -243,18 +244,28 @@ class Bookmark(): while sig == 'BKMK': # Fix start location for Highlights using BKMK data 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]) 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) - elif end_loc in user_notes and user_notes[end_loc]['type'] == 'Note': - # Skip duplicate bookmarks for notes - pass else: # If a bookmark coincides with a user annotation, the locs could # be the same - cheat by nudging -1 # Skip bookmark for last_read_location 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 user_notes[end_loc - 1] = dict(id=self.id, displayed_location=displayed_location, @@ -265,10 +276,43 @@ class Bookmark(): sig = data[eo:eo+4] elif self.bookmark_extension == 'tan': - # TAN bookmarks + from calibre.ebooks.metadata.topaz import get_metadata as get_topaz_metadata + + 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 Exception('error') + except: + text = '(Unable to extract highlight text from My Clippings.txt)' + return text + MAGIC_TOPAZ_CONSTANT = 33.33 - self.timestamp = os.path.getmtime(path) - with open(path,'rb') as f: + self.timestamp = os.path.getmtime(self.path) + with open(self.path,'rb') as f: stream = StringIO(f.read()) data = StreamSlicer(stream) self.last_read = int(unpack('>I', data[5:9])[0]) @@ -285,7 +329,7 @@ class Bookmark(): e_type = 'Bookmark' elif e_type == 1: e_type = 'Highlight' - text = "(Topaz highlights not yet supported)" + text = get_topaz_highlight(location/MAGIC_TOPAZ_CONSTANT + 1) elif e_type == 2: e_type = 'Note' text = data[e_base+0x10:e_base+0x10+text_len] @@ -293,10 +337,9 @@ class Bookmark(): e_type = 'Unknown annotation type' if self.book_format in ['tpz','azw1']: - # *** This needs fine-tuning displayed_location = location/MAGIC_TOPAZ_CONSTANT + 1 elif self.book_format == 'pdf': - # *** This needs testing + # *** This needs implementation displayed_location = location user_notes[location] = dict(id=self.id, displayed_location=displayed_location, @@ -315,16 +358,9 @@ class Bookmark(): print "unsupported bookmark_extension: %s" % self.bookmark_extension self.user_notes = user_notes - ''' - 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): + def get_book_length(self): 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 if self.bookmark_extension == 'mbp': diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index c266414e70..d630cfbf63 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -25,7 +25,7 @@ class DRMError(ValueError): BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm', 'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc', '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): diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index 27a61df23f..fd1a1c3947 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -336,6 +336,13 @@ class MetadataUpdater(object): if mi.publisher: update_exth_record((101, mi.publisher.encode(self.codec, 'replace'))) if mi.comments: + # Strip user annotations + a_offset = mi.comments.find('
') + ad_offset = mi.comments.find('
') + 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'))) if mi.isbn: update_exth_record((104, mi.isbn.encode(self.codec, 'replace'))) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 83629a55c4..b72ac7d1c4 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -1013,8 +1013,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): spanTag = Tag(ka_soup, 'span') spanTag['style'] = 'font-weight:bold' spanTag.insert(0,NavigableString("%s
Last Page Read: Location %d (%d%%)" % \ - (strftime(u'%x', timestamp.timetuple()), - last_read_location, percent_read))) + (strftime(u'%x', timestamp.timetuple()), + last_read_location, percent_read))) divTag.insert(dtc, spanTag) dtc += 1 @@ -1074,6 +1074,11 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): mi.comments = unicode(user_notes_soup.prettify()) # Update library 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_done.emit() self.done_callback(self.am.keys()) @@ -1516,6 +1521,12 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): opts = config().parse() if single_format is not None: 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: opts.template = opts.template.split('/')[-1].strip() if not opts.template: diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 025020848d..0a00b4c0e6 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1052,7 +1052,16 @@ class EPUB_MOBI(CatalogPlugin): this_title['rating'] = record['rating'] if record['rating'] else 0 this_title['date'] = strftime(u'%B %Y', record['pubdate'].timetuple()) this_title['timestamp'] = record['timestamp'] + if record['comments']: + # Strip annotations + a_offset = record['comments'].find('
') + ad_offset = record['comments'].find('
') + 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']) paras = BeautifulSoup(this_title['description']).findAll('p') tokens = []