mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
More catalog tweaks
This commit is contained in:
parent
ddb593a814
commit
4d1cfec950
@ -4,7 +4,7 @@ from collections import namedtuple
|
||||
from datetime import date
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
from calibre import filesystem_encoding, prints, strftime
|
||||
from calibre import filesystem_encoding, prints, prepare_string_for_xml, strftime
|
||||
from calibre.customize import CatalogPlugin
|
||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||
from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString
|
||||
@ -524,6 +524,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.opts.output_profile and \
|
||||
self.opts.output_profile.startswith("kindle")) else False
|
||||
self.__genres = None
|
||||
self.__genre_tags_dict = None
|
||||
self.__htmlFileList = []
|
||||
self.__markerTags = self.getMarkerTags()
|
||||
self.__ncxSoup = None
|
||||
@ -630,6 +631,13 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.__genres = val
|
||||
return property(fget=fget, fset=fset)
|
||||
@dynamic_property
|
||||
def genre_tags_dict(self):
|
||||
def fget(self):
|
||||
return self.__genre_tags_dict
|
||||
def fset(self, val):
|
||||
self.__genre_tags_dict = val
|
||||
return property(fget=fget, fset=fset)
|
||||
@dynamic_property
|
||||
def htmlFileList(self):
|
||||
def fget(self):
|
||||
return self.__htmlFileList
|
||||
@ -797,10 +805,13 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
os.path.join(self.catalogPath, file[0]))
|
||||
|
||||
# Create the custom masthead image overwriting default
|
||||
try:
|
||||
self.generate_masthead_image(os.path.join(self.catalogPath, 'images/mastheadImage.gif'))
|
||||
except:
|
||||
pass
|
||||
# If failure, default mastheadImage.gif should still be in place
|
||||
if self.generateForKindle:
|
||||
try:
|
||||
self.generate_masthead_image(os.path.join(self.catalogPath,
|
||||
'images/mastheadImage.gif'))
|
||||
except:
|
||||
pass
|
||||
|
||||
def fetchBooksByTitle(self):
|
||||
self.updateProgressFullStep("Fetching database")
|
||||
@ -853,7 +864,11 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
this_title['date'] = strftime(u'%B %Y', record['pubdate'].timetuple())
|
||||
this_title['timestamp'] = record['timestamp']
|
||||
if record['comments']:
|
||||
this_title['description'] = re.sub('&', '&', record['comments'])
|
||||
#this_title['description'] = re.sub('&', '&', record['comments'])
|
||||
if re.search('<(?P<tag>.+)>.+</(?P=tag)>|<!--.+-->|<.+/>',record['comments']):
|
||||
self.opts.log(" %d: %s (%s) contains suspect metadata" % \
|
||||
(this_title['id'], this_title['title'],this_title['author']))
|
||||
this_title['description'] = prepare_string_for_xml(record['comments'])
|
||||
this_title['short_description'] = self.generateShortDescription(this_title['description'])
|
||||
else:
|
||||
this_title['description'] = None
|
||||
@ -1003,7 +1018,8 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
for tag in title['tags']:
|
||||
aTag = Tag(soup,'a')
|
||||
aTag['href'] = "Genre%s.html" % re.sub("\W","",self.convertHTMLEntities(tag))
|
||||
#print "aTag: %s" % "Genre_%s.html" % re.sub("\W","",tag.lower())
|
||||
aTag['href'] = "Genre_%s.html" % re.sub("\W","",tag.lower())
|
||||
aTag.insert(0,escape(NavigableString(tag)))
|
||||
emTag = Tag(soup, "em")
|
||||
emTag.insert(0, aTag)
|
||||
@ -1435,17 +1451,22 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
self.updateProgressFullStep("'Genres'")
|
||||
|
||||
# Filter out REMOVE_TAGS, sort
|
||||
filtered_tags = self.filterDbTags(self.db.all_tags())
|
||||
# filtered_tags = {friendly:normalized, }
|
||||
self.genre_tags_dict = self.filterDbTags(self.db.all_tags())
|
||||
|
||||
# Extract books matching filtered_tags
|
||||
genre_list = []
|
||||
for tag in filtered_tags:
|
||||
for friendly_tag in self.genre_tags_dict:
|
||||
#print "\ngenerateHTMLByTags(): looking for books with friendly_tag '%s'" % friendly_tag
|
||||
# tag_list => {'tag': '<normalized_genre_tag>', 'books':[{}, {}, {}]}
|
||||
tag_list = {}
|
||||
tag_list['tag'] = tag
|
||||
tag_list['tag'] = self.genre_tags_dict[friendly_tag]
|
||||
tag_list['books'] = []
|
||||
for book in self.booksByAuthor:
|
||||
if 'tags' in book and tag in book['tags']:
|
||||
# Scan each book for tag matching friendly_tag
|
||||
#if 'tags' in book: print " evaluating %s with tags: %s" % (book['title'], book['tags'])
|
||||
if 'tags' in book and friendly_tag in book['tags']:
|
||||
#print " adding '%s'" % (book['title'])
|
||||
this_book = {}
|
||||
this_book['author'] = book['author']
|
||||
this_book['title'] = book['title']
|
||||
@ -1455,8 +1476,36 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
tag_list['books'].append(this_book)
|
||||
|
||||
if len(tag_list['books']):
|
||||
# Possible to have an empty tag list if the books were excluded
|
||||
genre_list.append(tag_list)
|
||||
genre_exists = False
|
||||
book_not_in_genre = True
|
||||
if not genre_list:
|
||||
#print " genre_list empty, adding '%s'" % tag_list['tag']
|
||||
genre_list.append(tag_list)
|
||||
else:
|
||||
# Check for existing_genre
|
||||
for genre in genre_list:
|
||||
if genre['tag'] == tag_list['tag']:
|
||||
genre_exists = True
|
||||
# Check to see if the book is already in this list
|
||||
for existing_book in genre['books']:
|
||||
if this_book['title'] == existing_book['title']:
|
||||
#print "%s already in %s" % (this_book['title'], genre)
|
||||
book_not_in_genre = False
|
||||
break
|
||||
break
|
||||
|
||||
if genre_exists:
|
||||
if book_not_in_genre:
|
||||
#print " adding %s to existing genre '%s'" % (this_book['title'],genre['tag'])
|
||||
genre['books'].append(this_book)
|
||||
else:
|
||||
#print " appending genre '%s'" % tag_list['tag']
|
||||
genre_list.append(tag_list)
|
||||
|
||||
if self.opts.verbose:
|
||||
self.opts.log.info(" Genre summary: %d active genres" % len(genre_list))
|
||||
for genre in genre_list:
|
||||
self.opts.log.info(" %s: %d titles" % (genre['tag'], len(genre['books'])))
|
||||
|
||||
# Write the results
|
||||
# genre_list = [ [tag_list], [tag_list] ...]
|
||||
@ -1489,12 +1538,11 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
if not author in unique_authors:
|
||||
unique_authors.append(author)
|
||||
'''
|
||||
|
||||
# Write the genre book list as an article
|
||||
titles_spanned = self.generateHTMLByGenre(genre['tag'], True if index==0 else False, genre['books'],
|
||||
"%s/Genre%s.html" % (self.contentDir, re.sub("\W","", self.convertHTMLEntities(genre['tag']))))
|
||||
"%s/Genre_%s.html" % (self.contentDir, genre['tag']))
|
||||
|
||||
tag_file = "content/Genre%s.html" % (re.sub("\W","", self.convertHTMLEntities(genre['tag'])))
|
||||
tag_file = "content/Genre_%s.html" % genre['tag']
|
||||
master_genre_list.append({'tag':genre['tag'],
|
||||
'file':tag_file,
|
||||
'authors':unique_authors,
|
||||
@ -1586,7 +1634,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
def generateOPF(self):
|
||||
|
||||
self.updateProgressFullStep("Saving OPF")
|
||||
self.updateProgressFullStep("Generating OPF")
|
||||
|
||||
header = '''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
@ -2107,9 +2155,6 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
self.updateProgressFullStep("NCX 'Genres'")
|
||||
|
||||
|
||||
|
||||
|
||||
if not len(self.genres):
|
||||
self.opts.log.warn(" No genres found in tags.\n"
|
||||
" No Genre section added to Catalog")
|
||||
@ -2136,13 +2181,12 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
navPointTag.insert(nptc, navLabelTag)
|
||||
nptc += 1
|
||||
contentTag = Tag(ncx_soup,"content")
|
||||
contentTag['src'] = "content/Genre%s.html#section_start" % (re.sub("\W","", self.convertHTMLEntities(self.genres[0]['tag'])))
|
||||
contentTag['src'] = "content/Genre_%s.html#section_start" % self.genres[0]['tag']
|
||||
navPointTag.insert(nptc, contentTag)
|
||||
nptc += 1
|
||||
|
||||
for genre in self.genres:
|
||||
# Add an article for each genre
|
||||
|
||||
navPointVolumeTag = Tag(ncx_soup, 'navPoint')
|
||||
navPointVolumeTag['class'] = "article"
|
||||
navPointVolumeTag['id'] = "genre-%s-ID" % genre['tag']
|
||||
@ -2150,13 +2194,18 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.playOrder += 1
|
||||
navLabelTag = Tag(ncx_soup, "navLabel")
|
||||
textTag = Tag(ncx_soup, "text")
|
||||
textTag.insert(0, self.formatNCXText(NavigableString(genre['tag'])))
|
||||
|
||||
# GwR *** Can this be optimized?
|
||||
normalized_tag = None
|
||||
for genre_tag in self.genre_tags_dict:
|
||||
if self.genre_tags_dict[genre_tag] == genre['tag']:
|
||||
normalized_tag = self.genre_tags_dict[genre_tag]
|
||||
break
|
||||
textTag.insert(0, self.formatNCXText(NavigableString(genre_tag)))
|
||||
navLabelTag.insert(0,textTag)
|
||||
navPointVolumeTag.insert(0,navLabelTag)
|
||||
|
||||
contentTag = Tag(ncx_soup, "content")
|
||||
genre_name = re.sub("\W","", self.convertHTMLEntities(genre['tag']))
|
||||
contentTag['src'] = "content/Genre%s.html#Genre%s" % (genre_name, genre_name)
|
||||
contentTag['src'] = "content/Genre_%s.html#Genre_%s" % (normalized_tag, normalized_tag)
|
||||
navPointVolumeTag.insert(1, contentTag)
|
||||
|
||||
if self.generateForKindle:
|
||||
@ -2268,16 +2317,10 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
def filterDbTags(self, tags):
|
||||
# Remove the special marker tags from the database's tag list,
|
||||
# return sorted list of tags representing valid genres
|
||||
# return sorted list of normalized genre tags
|
||||
|
||||
def next_tag(tags):
|
||||
for (i, tag) in enumerate(tags):
|
||||
if i < len(tags) - 1:
|
||||
yield tag + ", "
|
||||
else:
|
||||
yield tag
|
||||
|
||||
filtered_tags = []
|
||||
normalized_tags = []
|
||||
friendly_tags = []
|
||||
for tag in tags:
|
||||
if tag[0] in self.markerTags:
|
||||
continue
|
||||
@ -2286,32 +2329,39 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
if tag == ' ':
|
||||
continue
|
||||
|
||||
filtered_tags.append(tag)
|
||||
normalized_tags.append(re.sub('\W','',tag).lower())
|
||||
friendly_tags.append(tag)
|
||||
|
||||
filtered_tags.sort()
|
||||
|
||||
# Enable this code to force certain tags to the front of the genre list
|
||||
if False:
|
||||
for (i, tag) in enumerate(filtered_tags):
|
||||
if tag == 'Fiction':
|
||||
filtered_tags.insert(0, (filtered_tags.pop(i)))
|
||||
elif tag == 'Nonfiction':
|
||||
filtered_tags.insert(1, (filtered_tags.pop(i)))
|
||||
else:
|
||||
continue
|
||||
genre_tags_dict = dict(zip(friendly_tags,normalized_tags))
|
||||
|
||||
# Test for multiple genres resolving to same normalized form
|
||||
normalized_set = set(normalized_tags)
|
||||
for normalized in normalized_set:
|
||||
if normalized_tags.count(normalized) > 1:
|
||||
self.opts.log.warn(" Warning: multiple tags resolving to genre '%s':" % normalized)
|
||||
for key in genre_tags_dict:
|
||||
if genre_tags_dict[key] == normalized:
|
||||
self.opts.log.warn(" %s" % key)
|
||||
if self.verbose:
|
||||
self.opts.log.info(u' %d Genre tags in database (exclude_genre: %s):' % \
|
||||
(len(filtered_tags), self.opts.exclude_genre))
|
||||
out_buf = ''
|
||||
def next_tag(tags):
|
||||
for (i, tag) in enumerate(tags):
|
||||
if i < len(tags) - 1:
|
||||
yield tag + ", "
|
||||
else:
|
||||
yield tag
|
||||
|
||||
for tag in next_tag(filtered_tags):
|
||||
out_buf += tag
|
||||
if len(out_buf) > 72:
|
||||
self.opts.log(u' %s' % out_buf.rstrip())
|
||||
out_buf = ''
|
||||
self.opts.log(u' %s' % out_buf)
|
||||
self.opts.log.info(u' %d total genre tags in database (exclude_genre: %s):' % \
|
||||
(len(genre_tags_dict), self.opts.exclude_genre))
|
||||
|
||||
return filtered_tags
|
||||
# Display friendly/normalized genres
|
||||
# friendly => normalized
|
||||
sorted_tags = ['%s => %s' % (key, genre_tags_dict[key]) for key in sorted(genre_tags_dict.keys())]
|
||||
|
||||
for tag in next_tag(sorted_tags):
|
||||
self.opts.log(u' %s' % tag)
|
||||
|
||||
return genre_tags_dict
|
||||
|
||||
def formatNCXText(self, description):
|
||||
# Kindle TOC descriptions won't render certain characters
|
||||
@ -2343,15 +2393,23 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
body.insert(btc, aTag)
|
||||
btc += 1
|
||||
|
||||
# Insert the anchor with spaces stripped
|
||||
# Create an anchor from the tag
|
||||
aTag = Tag(soup, 'a')
|
||||
aTag['name'] = "Genre%s" % re.sub("\W","", genre)
|
||||
#aTag['name'] = "Genre%s" % re.sub("\W","", genre)
|
||||
aTag['name'] = "Genre_%s" % genre
|
||||
body.insert(btc,aTag)
|
||||
btc += 1
|
||||
|
||||
# Insert the genre title
|
||||
# Insert the genre title using the friendly name
|
||||
# GwR *** optimize
|
||||
for genre_tag in self.genre_tags_dict:
|
||||
if self.genre_tags_dict[genre_tag] == genre:
|
||||
friendly_tag = genre_tag
|
||||
break
|
||||
|
||||
|
||||
titleTag = body.find(attrs={'class':'title'})
|
||||
titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(genre)))
|
||||
titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(friendly_tag)))
|
||||
|
||||
# Insert the books by author list
|
||||
divTag = body.find(attrs={'class':'authors'})
|
||||
@ -2512,6 +2570,8 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
return soup
|
||||
|
||||
def generate_masthead_image(self, out_path):
|
||||
font_path = P('fonts/liberation/LiberationSerif-Bold.ttf')
|
||||
|
||||
MI_WIDTH = 600
|
||||
MI_HEIGHT = 60
|
||||
|
||||
@ -2523,7 +2583,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
img = Image.new('RGB', (MI_WIDTH, MI_HEIGHT), 'white')
|
||||
draw = ImageDraw.Draw(img)
|
||||
font = ImageFont.truetype(P('fonts/liberation/LiberationSerif-Bold.ttf'), 48)
|
||||
font = ImageFont.truetype(font_path, 48)
|
||||
text = self.title.encode('utf-8')
|
||||
width, height = draw.textsize(text, font=font)
|
||||
left = max(int((MI_WIDTH - width)/2.), 0)
|
||||
@ -2671,7 +2731,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
opts.descriptionClip = 380 if op.endswith('dx') or 'kindle' not in op else 90
|
||||
opts.basename = "Catalog"
|
||||
opts.plugin_path = self.plugin_path
|
||||
opts.cli_environment = getattr(opts,'sync',True)
|
||||
opts.cli_environment = not hasattr(opts,'sync')
|
||||
|
||||
if opts.verbose:
|
||||
opts_dict = vars(opts)
|
||||
|
Loading…
x
Reference in New Issue
Block a user