From b817c151b5c28617a42f99706259b660913fffb1 Mon Sep 17 00:00:00 2001 From: GRiker Date: Thu, 11 Feb 2010 07:15:55 -0700 Subject: [PATCH] GwR update author lists, fix EXTH output --- resources/catalog/stylesheet.css | 6 ++ src/calibre/ebooks/metadata/mobi.py | 33 +++++- src/calibre/library/catalog.py | 152 +++++++++++++++++++--------- 3 files changed, 137 insertions(+), 54 deletions(-) diff --git a/resources/catalog/stylesheet.css b/resources/catalog/stylesheet.css index 12063b121f..06bbf8eaab 100644 --- a/resources/catalog/stylesheet.css +++ b/resources/catalog/stylesheet.css @@ -79,3 +79,9 @@ p.unread_book { text-indent:-2em; } +hr.series_divider { + width:50%; + margin-left:1em; + margin-top:0em; + margin-bottom:0em; + } diff --git a/src/calibre/ebooks/metadata/mobi.py b/src/calibre/ebooks/metadata/mobi.py index 63d55fa9a4..d1a01d20c1 100644 --- a/src/calibre/ebooks/metadata/mobi.py +++ b/src/calibre/ebooks/metadata/mobi.py @@ -105,13 +105,14 @@ class MetadataUpdater(object): have_exth = self.have_exth = (flags & 0x40) != 0 self.cover_record = self.thumbnail_record = None self.timestamp = None - self.pdbrecords = self.get_pdbrecords() + + self.original_exth_records = {} if not have_exth: self.create_exth() - - # Fetch timestamp, cover_record, thumbnail_record - self.fetchEXTHFields() + else: + # Fetch timestamp, cover_record, thumbnail_record + self.fetchEXTHFields() def fetchEXTHFields(self): stream = self.stream @@ -131,6 +132,8 @@ class MetadataUpdater(object): content = exth[pos + 8: pos + size] pos += size + self.original_exth_records[id] = content + if id == 106: self.timestamp = content elif id == 201: @@ -284,6 +287,10 @@ class MetadataUpdater(object): return StreamSlicer(self.stream, start, stop) def update(self, mi): + def pop_exth_record(exth_id): + if exth_id in self.original_exth_records: + self.original_exth_records.pop(exth_id) + if self.type != "BOOKMOBI": raise MobiError("Setting metadata only supported for MOBI files of type 'BOOK'.\n" "\tThis is a '%s' file of type '%s'" % (self.type[0:4], self.type[4:8])) @@ -298,35 +305,53 @@ class MetadataUpdater(object): if mi.author_sort and pas: authors = mi.author_sort recs.append((100, authors.encode(self.codec, 'replace'))) + pop_exth_record(100) elif mi.authors: authors = '; '.join(mi.authors) recs.append((100, authors.encode(self.codec, 'replace'))) + pop_exth_record(100) if mi.publisher: recs.append((101, mi.publisher.encode(self.codec, 'replace'))) + pop_exth_record(101) if mi.comments: recs.append((103, mi.comments.encode(self.codec, 'replace'))) + pop_exth_record(103) if mi.isbn: recs.append((104, mi.isbn.encode(self.codec, 'replace'))) + pop_exth_record(104) if mi.tags: subjects = '; '.join(mi.tags) recs.append((105, subjects.encode(self.codec, 'replace'))) + pop_exth_record(105) if mi.pubdate: recs.append((106, str(mi.pubdate).encode(self.codec, 'replace'))) + pop_exth_record(106) elif mi.timestamp: recs.append((106, str(mi.timestamp).encode(self.codec, 'replace'))) + pop_exth_record(106) elif self.timestamp: recs.append((106, self.timestamp)) + pop_exth_record(106) else: recs.append((106, str(datetime.now()).encode(self.codec, 'replace'))) + pop_exth_record(106) if self.cover_record is not None: recs.append((201, pack('>I', self.cover_rindex))) recs.append((203, pack('>I', 0))) + pop_exth_record(201) + pop_exth_record(203) if self.thumbnail_record is not None: recs.append((202, pack('>I', self.thumbnail_rindex))) + pop_exth_record(202) if getattr(self, 'encryption_type', -1) != 0: raise MobiError('Setting metadata in DRMed MOBI files is not supported.') + # Restore any original EXTH fields that weren't modified/updated + for id in sorted(self.original_exth_records): + recs.append((id, self.original_exth_records[id])) + recs = sorted(recs, key=lambda x:(x[0],x[0])) + exth = StringIO() for code, data in recs: exth.write(pack('>II', code, len(data) + 8)) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 78ec63dcf7..893c7dd1c9 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -941,12 +941,23 @@ class EPUB_MOBI(CatalogPlugin): this_title['series_index'] = 0.0 this_title['title_sort'] = self.generateSortTitle(this_title['title']) - if 'authors' in record and record['authors'] is not None: + if 'authors' in record and record['authors']: this_title['author'] = " & ".join(record['authors']) else: this_title['author'] = 'Unknown' - this_title['author_sort'] = record['author_sort'].title() if len(record['author_sort'].strip()) \ + + ''' + this_title['author_sort_original'] = record['author_sort'] + author_sort = record['author_sort'] if len(record['author_sort'].strip()) \ else self.author_to_author_sort(this_title['author']) + author_sort = author_sort[0].upper() + author_sort[1:] + this_title['author_sort'] = author_sort + ''' + if 'author_sort' in record and record['author_sort'].strip(): + this_title['author_sort'] = record['author_sort'] + else: + this_title['author_sort'] = self.author_to_author_sort(this_title['author']) + this_title['id'] = record['id'] if record['publisher']: this_title['publisher'] = re.sub('&', '&', record['publisher']) @@ -996,37 +1007,6 @@ class EPUB_MOBI(CatalogPlugin): def fetchBooksByAuthor(self): # Generate a list of titles sorted by author from the database - def author_compare(x,y): - # Return -1 if xy - # Different authors - sort by author_sort - if x['author_sort'] > y['author_sort']: - return 1 - elif x['author_sort'] < y['author_sort']: - return -1 - else: - # Same author - if x['series'] != y['series']: - # Different series - if x['title_sort'].lstrip() > y['title_sort'].lstrip(): - return 1 - else: - return -1 - else: - # Same series - if x['series'] == y['series']: - if float(x['series_index']) > float(y['series_index']): - return 1 - elif float(x['series_index']) < float(y['series_index']): - return -1 - else: - return 0 - else: - if x['series'] > y['series']: - return 1 - else: - return -1 self.updateProgressFullStep("Sorting database") @@ -1037,13 +1017,13 @@ class EPUB_MOBI(CatalogPlugin): ''' self.booksByAuthor = list(self.booksByTitle) - self.booksByAuthor.sort(author_compare) + self.booksByAuthor.sort(self.author_compare) if False and self.verbose: self.opts.log.info("fetchBooksByAuthor(): %d books" % len(self.booksByAuthor)) - self.opts.log.info(" %-30s %-20s %s" % ('title', 'title_sort','series', 'series_index')) + self.opts.log.info(" %-30s %-20s %s" % ('title', 'series', 'series_index')) for title in self.booksByAuthor: - self.opts.log.info((u" %-30s %-20s %-20s%5s " % \ + self.opts.log.info((u" %-30s %-20s%5s " % \ (title['title'][:30], title['series'][:20] if title['series'] else '', title['series_index'], @@ -1051,7 +1031,7 @@ class EPUB_MOBI(CatalogPlugin): raise SystemExit # Build the unique_authors set from existing data - authors = [(record['author'], record['author_sort']) for record in self.booksByAuthor] + authors = [(record['author'], record['author_sort'].capitalize()) for record in self.booksByAuthor] # authors[] contains a list of all book authors, with multiple entries for multiple books by author # authors[]: (([0]:friendly [1]:sort)) @@ -1417,7 +1397,7 @@ class EPUB_MOBI(CatalogPlugin): book_count = 0 for book in self.booksByAuthor: book_count += 1 - if book['author_sort'][0].upper() != current_letter : + if self.letter_or_symbol(book['author_sort'][0].upper()) != current_letter : ''' # Start a new letter - anchor only, hidden current_letter = book['author_sort'][0].upper() @@ -1427,19 +1407,21 @@ class EPUB_MOBI(CatalogPlugin): dtc += 1 ''' # Start a new letter with Index letter - current_letter = book['author_sort'][0].upper() + current_letter = self.letter_or_symbol(book['author_sort'][0].upper()) pIndexTag = Tag(soup, "p") pIndexTag['class'] = "letter_index" aTag = Tag(soup, "a") - aTag['name'] = "%sauthors" % current_letter + aTag['name'] = "%sauthors" % self.letter_or_symbol(current_letter) pIndexTag.insert(0,aTag) - pIndexTag.insert(1,NavigableString(book['author_sort'][0].upper())) + pIndexTag.insert(1,NavigableString(self.letter_or_symbol(book['author_sort'][0].upper()))) divTag.insert(dtc,pIndexTag) dtc += 1 if book['author'] != current_author: # Start a new author current_author = book['author'] + non_series_books = 0 + current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" emTag = Tag(soup, "em") @@ -1451,6 +1433,14 @@ class EPUB_MOBI(CatalogPlugin): divTag.insert(dtc,pAuthorTag) dtc += 1 + # Insert an
between non-series and series + if not current_series and non_series_books and book['series']: + # Insert an
+ hrTag = Tag(soup,'hr') + hrTag['class'] = "series_divider" + divTag.insert(dtc,hrTag) + dtc += 1 + # Check for series if book['series'] and book['series'] != current_series: # Start a new series @@ -1486,6 +1476,7 @@ class EPUB_MOBI(CatalogPlugin): aTag.insert(0,escape(book['title'][len(book['series'])+1:])) else: aTag.insert(0,escape(book['title'])) + non_series_books += 1 pBookTag.insert(ptc, aTag) ptc += 1 @@ -1522,12 +1513,11 @@ class EPUB_MOBI(CatalogPlugin): def add_books_to_HTML(this_months_list, dtc): if len(this_months_list): - date_string = strftime(u'%B %Y', current_date.timetuple()) - this_months_list = sorted(this_months_list, - key=lambda x:(x['title_sort'], x['title_sort'])) - this_months_list = sorted(this_months_list, - key=lambda x:(x['author_sort'], x['author_sort'])) + + this_months_list.sort(self.author_compare) + # Create a new month anchor + date_string = strftime(u'%B %Y', current_date.timetuple()) pIndexTag = Tag(soup, "p") pIndexTag['class'] = "date_index" aTag = Tag(soup, "a") @@ -1543,6 +1533,8 @@ class EPUB_MOBI(CatalogPlugin): if new_entry['author'] != current_author: # Start a new author current_author = new_entry['author'] + non_series_books = 0 + current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" emTag = Tag(soup, "em") @@ -1554,6 +1546,14 @@ class EPUB_MOBI(CatalogPlugin): divTag.insert(dtc,pAuthorTag) dtc += 1 + # Insert an
between non-series and series + if not current_series and non_series_books and new_entry['series']: + # Insert an
+ hrTag = Tag(soup,'hr') + hrTag['class'] = "series_divider" + divTag.insert(dtc,hrTag) + dtc += 1 + # Check for series if new_entry['series'] and new_entry['series'] != current_series: # Start a new series @@ -1588,6 +1588,7 @@ class EPUB_MOBI(CatalogPlugin): aTag.insert(0,escape(new_entry['title'][len(new_entry['series'])+1:])) else: aTag.insert(0,escape(new_entry['title'])) + non_series_books += 1 pBookTag.insert(ptc, aTag) ptc += 1 @@ -1685,7 +1686,7 @@ class EPUB_MOBI(CatalogPlugin): this_book = {} this_book['author'] = book['author'] this_book['title'] = book['title'] - this_book['author_sort'] = book['author_sort'] + this_book['author_sort'] = book['author_sort'].capitalize() this_book['read'] = book['read'] this_book['id'] = book['id'] this_book['series'] = book['series'] @@ -2228,15 +2229,15 @@ class EPUB_MOBI(CatalogPlugin): # self.authors[0]:friendly [1]:author_sort [2]:book_count master_author_list = [] # self.authors[0][1][0] = Initial letter of author_sort[0] - current_letter = self.authors[0][1][0] + current_letter = self.letter_or_symbol(self.authors[0][1][0]) current_author_list = [] for author in self.authors: - if author[1][0] != current_letter: + if self.letter_or_symbol(author[1][0]) != current_letter: # Save the old list add_to_author_list(current_author_list, current_letter) # Start the new list - current_letter = author[1][0] + current_letter = self.letter_or_symbol(author[1][0]) current_author_list = [author[0]] else: if len(current_author_list) < self.descriptionClip: @@ -2496,6 +2497,46 @@ class EPUB_MOBI(CatalogPlugin): tokens[0] += ',' return ' '.join(tokens).capitalize() + def author_compare(self,x,y): + # Return -1 if xy + + # Different authors - sort by author_sort + if x['author_sort'].capitalize() > y['author_sort'].capitalize(): + return 1 + elif x['author_sort'].capitalize() < y['author_sort'].capitalize(): + return -1 + else: + # Same author + if x['series'] != y['series']: + # One title is a series, the other is not + if not x['series']: + # Sort regular titles < series titles + return -1 + elif not y['series']: + return 1 + + # Different series + if x['title_sort'].lstrip() > y['title_sort'].lstrip(): + return 1 + else: + return -1 + else: + # Same series + if x['series'] == y['series']: + if float(x['series_index']) > float(y['series_index']): + return 1 + elif float(x['series_index']) < float(y['series_index']): + return -1 + else: + return 0 + else: + if x['series'] > y['series']: + return 1 + else: + return -1 + def calculateThumbnailSize(self): ''' Calculate thumbnail dimensions based on device DPI. Scale Kindle by 50% ''' from calibre.customize.ui import output_profiles @@ -2658,6 +2699,8 @@ class EPUB_MOBI(CatalogPlugin): if book['author'] != current_author: # Start a new author with link current_author = book['author'] + non_series_books = 0 + current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" emTag = Tag(soup, "em") @@ -2669,6 +2712,14 @@ class EPUB_MOBI(CatalogPlugin): divTag.insert(dtc,pAuthorTag) dtc += 1 + # Insert an
between non-series and series + if not current_series and non_series_books and book['series']: + # Insert an
+ hrTag = Tag(soup,'hr') + hrTag['class'] = "series_divider" + divTag.insert(dtc,hrTag) + dtc += 1 + # Check for series if book['series'] and book['series'] != current_series: # Start a new series @@ -2703,6 +2754,7 @@ class EPUB_MOBI(CatalogPlugin): aTag.insert(0,escape(book['title'][len(book['series'])+1:])) else: aTag.insert(0,escape(book['title'])) + non_series_books += 1 pBookTag.insert(ptc, aTag) ptc += 1