mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Added series to descriptions/titles
This commit is contained in:
parent
b1852e0e3b
commit
cea60d5fd8
@ -17,6 +17,14 @@ p.author {
|
|||||||
font-size:large;
|
font-size:large;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.series {
|
||||||
|
margin-top:0em;
|
||||||
|
margin-bottom:0em;
|
||||||
|
text-align: left;
|
||||||
|
text-indent: 1em;
|
||||||
|
font-size:small;
|
||||||
|
}
|
||||||
|
|
||||||
p.tags {
|
p.tags {
|
||||||
margin-top:0em;
|
margin-top:0em;
|
||||||
margin-bottom:0em;
|
margin-bottom:0em;
|
||||||
@ -27,7 +35,7 @@ p.tags {
|
|||||||
|
|
||||||
p.description {
|
p.description {
|
||||||
text-align:left;
|
text-align:left;
|
||||||
font-style:italic;
|
font-style:normal;
|
||||||
margin-top: 0em;
|
margin-top: 0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -927,8 +927,16 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
for record in data:
|
for record in data:
|
||||||
this_title = {}
|
this_title = {}
|
||||||
|
|
||||||
title = this_title['title'] = self.convertHTMLEntities(record['title'])
|
this_title['title'] = self.convertHTMLEntities(record['title'])
|
||||||
this_title['title_sort'] = self.generateSortTitle(title)
|
if record['series']:
|
||||||
|
this_title['series'] = record['series']
|
||||||
|
this_title['series_index'] = record['series_index']
|
||||||
|
this_title['title'] = self.generateSeriesTitle(this_title)
|
||||||
|
else:
|
||||||
|
this_title['series'] = None
|
||||||
|
this_title['series_index'] = 0.0
|
||||||
|
|
||||||
|
this_title['title_sort'] = self.generateSortTitle(this_title['title'])
|
||||||
if 'authors' in record and len(record['authors']):
|
if 'authors' in record and len(record['authors']):
|
||||||
this_title['author'] = " & ".join(record['authors'])
|
this_title['author'] = " & ".join(record['authors'])
|
||||||
else:
|
else:
|
||||||
@ -984,12 +992,61 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
def fetchBooksByAuthor(self):
|
def fetchBooksByAuthor(self):
|
||||||
# Generate a list of titles sorted by author from the database
|
# Generate a list of titles sorted by author from the database
|
||||||
|
def author_compare(x,y):
|
||||||
|
# Return -1 if x<y
|
||||||
|
# Return 0 if x==y
|
||||||
|
# Return 1 if x>y
|
||||||
|
#print "x['author_sort']: %s y['author_sort']: %s" % (x['author_sort'],y['author_sort'])
|
||||||
|
if x['author_sort'] > y['author_sort']:
|
||||||
|
return 1
|
||||||
|
elif x['author_sort'] < y['author_sort']:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
# Authors equal
|
||||||
|
# Books w/o series go first
|
||||||
|
if x['series'] > y['series']:
|
||||||
|
return 1
|
||||||
|
elif x['series'] < y['series']:
|
||||||
|
return -1
|
||||||
|
elif not x['series'] and not y['series']:
|
||||||
|
if x['title'] > y['title']:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
else:
|
||||||
|
# Both books have 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")
|
self.updateProgressFullStep("Sorting database")
|
||||||
|
|
||||||
# Sort titles case-insensitive
|
'''
|
||||||
|
# Sort titles case-insensitive, by author
|
||||||
self.booksByAuthor = sorted(self.booksByTitle,
|
self.booksByAuthor = sorted(self.booksByTitle,
|
||||||
key=lambda x:(x['author_sort'].upper(), x['author_sort'].upper()))
|
key=lambda x:(x['author_sort'].upper(), x['author_sort'].upper()))
|
||||||
|
'''
|
||||||
|
|
||||||
|
self.booksByAuthor = list(self.booksByTitle)
|
||||||
|
self.booksByAuthor.sort(author_compare)
|
||||||
|
|
||||||
|
if False and self.verbose:
|
||||||
|
self.opts.log.info("fetchBooksByAuthor(): %d books" % len(self.booksByAuthor))
|
||||||
|
self.opts.log.info(" %-40s %-20s %s" % ('title', 'series', 'series_index'))
|
||||||
|
for title in self.booksByAuthor:
|
||||||
|
self.opts.log.info((u" %-40s %-20s %s" % \
|
||||||
|
(title['title'][0:40],
|
||||||
|
title['series'][0:20] if title['series'] else '',
|
||||||
|
title['series_index'])).encode('utf-8'))
|
||||||
|
|
||||||
# Build the unique_authors set from existing data
|
# 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']) for record in self.booksByAuthor]
|
||||||
@ -1063,6 +1120,14 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
# Insert the book title
|
# Insert the book title
|
||||||
#<p class="title"><a name="<database_id>"></a><em>Book Title</em></p>
|
#<p class="title"><a name="<database_id>"></a><em>Book Title</em></p>
|
||||||
emTag = Tag(soup, "em")
|
emTag = Tag(soup, "em")
|
||||||
|
if title['series']:
|
||||||
|
# Insert br at colon
|
||||||
|
brTag = Tag(soup,'br')
|
||||||
|
title_tokens = title['title'].split(': ')
|
||||||
|
emTag.insert(0, title_tokens[0] + ':')
|
||||||
|
emTag.insert(1, brTag)
|
||||||
|
emTag.insert(2, title_tokens[1])
|
||||||
|
else:
|
||||||
emTag.insert(0, NavigableString(escape(title['title'])))
|
emTag.insert(0, NavigableString(escape(title['title'])))
|
||||||
titleTag = body.find(attrs={'class':'title'})
|
titleTag = body.find(attrs={'class':'title'})
|
||||||
titleTag.insert(0,emTag)
|
titleTag.insert(0,emTag)
|
||||||
@ -1085,6 +1150,27 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
tagsTag.insert(0,emTag)
|
tagsTag.insert(0,emTag)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
'''
|
||||||
|
# Insert Series info or remove.
|
||||||
|
seriesTag = body.find(attrs={'class':'series'})
|
||||||
|
if title['series']:
|
||||||
|
# Insert a spacer to match the author indent
|
||||||
|
stc = 0
|
||||||
|
fontTag = Tag(soup,"font")
|
||||||
|
fontTag['style'] = 'color:white;font-size:large'
|
||||||
|
if self.opts.fmt == 'epub':
|
||||||
|
fontTag['style'] += ';opacity: 0.0'
|
||||||
|
fontTag.insert(0, NavigableString("by "))
|
||||||
|
seriesTag.insert(stc, fontTag)
|
||||||
|
stc += 1
|
||||||
|
if float(title['series_index']) - int(title['series_index']):
|
||||||
|
series_str = 'Series: %s [%4.2f]' % (title['series'], title['series_index'])
|
||||||
|
else:
|
||||||
|
series_str = '%s [%d]' % (title['series'], title['series_index'])
|
||||||
|
seriesTag.insert(stc,NavigableString(series_str))
|
||||||
|
else:
|
||||||
|
seriesTag.extract()
|
||||||
|
'''
|
||||||
# Insert linked genres
|
# Insert linked genres
|
||||||
if 'tags' in title:
|
if 'tags' in title:
|
||||||
tagsTag = body.find(attrs={'class':'tags'})
|
tagsTag = body.find(attrs={'class':'tags'})
|
||||||
@ -1367,6 +1453,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
|
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
|
||||||
|
# Use series, series index if avail else just title
|
||||||
aTag.insert(0,escape(book['title']))
|
aTag.insert(0,escape(book['title']))
|
||||||
pBookTag.insert(ptc, aTag)
|
pBookTag.insert(ptc, aTag)
|
||||||
ptc += 1
|
ptc += 1
|
||||||
@ -1786,7 +1873,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
mtc += 1
|
mtc += 1
|
||||||
|
|
||||||
# HTML files - add books to manifest and spine
|
# HTML files - add books to manifest and spine
|
||||||
for book in self.booksByTitle:
|
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
|
||||||
|
else self.booksByTitle
|
||||||
|
for book in sort_descriptions_by:
|
||||||
# manifest
|
# manifest
|
||||||
itemTag = Tag(soup, "item")
|
itemTag = Tag(soup, "item")
|
||||||
itemTag['href'] = "content/book_%d.html" % int(book['id'])
|
itemTag['href'] = "content/book_%d.html" % int(book['id'])
|
||||||
@ -1912,7 +2001,9 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
nptc += 1
|
nptc += 1
|
||||||
|
|
||||||
# Loop over the titles
|
# Loop over the titles
|
||||||
for book in self.booksByTitle:
|
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
|
||||||
|
else self.booksByTitle
|
||||||
|
for book in sort_descriptions_by:
|
||||||
navPointVolumeTag = Tag(ncx_soup, 'navPoint')
|
navPointVolumeTag = Tag(ncx_soup, 'navPoint')
|
||||||
navPointVolumeTag['class'] = "article"
|
navPointVolumeTag['class'] = "article"
|
||||||
navPointVolumeTag['id'] = "book%dID" % int(book['id'])
|
navPointVolumeTag['id'] = "book%dID" % int(book['id'])
|
||||||
@ -2553,6 +2644,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
<p class="title"></p>
|
<p class="title"></p>
|
||||||
{0}
|
{0}
|
||||||
<p class="author"></p>
|
<p class="author"></p>
|
||||||
|
<!--p class="series"></p-->
|
||||||
<p class="tags"> </p>
|
<p class="tags"> </p>
|
||||||
<table width="100%" border="0">
|
<table width="100%" border="0">
|
||||||
<tr>
|
<tr>
|
||||||
@ -2678,6 +2770,17 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
draw.text((left, top), text, fill=(0,0,0), font=font)
|
draw.text((left, top), text, fill=(0,0,0), font=font)
|
||||||
img.save(open(out_path, 'wb'), 'GIF')
|
img.save(open(out_path, 'wb'), 'GIF')
|
||||||
|
|
||||||
|
def generateSeriesTitle(self, title):
|
||||||
|
if float(title['series_index']) - int(title['series_index']):
|
||||||
|
series_title = '%s %4.2f: %s' % (title['series'],
|
||||||
|
title['series_index'],
|
||||||
|
title['title'])
|
||||||
|
else:
|
||||||
|
series_title = '%s %d: %s' % (title['series'],
|
||||||
|
title['series_index'],
|
||||||
|
title['title'])
|
||||||
|
return series_title
|
||||||
|
|
||||||
def generateShortDescription(self, description):
|
def generateShortDescription(self, description):
|
||||||
# Truncate the description to description_clip, on word boundaries if necessary
|
# Truncate the description to description_clip, on word boundaries if necessary
|
||||||
if not description:
|
if not description:
|
||||||
@ -2777,24 +2880,65 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
|
|
||||||
def markdownComments(self, comments):
|
def markdownComments(self, comments):
|
||||||
''' Convert random comment text to normalized, xml-legal block of <p>s'''
|
''' Convert random comment text to normalized, xml-legal block of <p>s'''
|
||||||
# reformat illegal xml
|
|
||||||
desc = prepare_string_for_xml(comments)
|
|
||||||
|
|
||||||
# normalize <br/> tags
|
|
||||||
desc = re.sub(r'<br[/]{0,1}>', '<br/>', desc)
|
|
||||||
|
|
||||||
# tokenize double line breaks
|
|
||||||
desc = comments.replace('\r', '')
|
|
||||||
tokens = comments.split('\n\n')
|
|
||||||
|
|
||||||
|
comments = comments.replace('\r', '')
|
||||||
|
if re.search('\n\n', comments):
|
||||||
soup = BeautifulSoup()
|
soup = BeautifulSoup()
|
||||||
ptc = 0
|
split_ps = comments.split('\n\n')
|
||||||
for token in tokens:
|
tsc = 0
|
||||||
|
for p in split_ps:
|
||||||
pTag = Tag(soup,'p')
|
pTag = Tag(soup,'p')
|
||||||
pTag.insert(0,token)
|
pTag.insert(0,p)
|
||||||
soup.insert(ptc, pTag)
|
soup.insert(tsc,pTag)
|
||||||
|
tsc += 1
|
||||||
|
else:
|
||||||
|
soup = BeautifulSoup(comments)
|
||||||
|
|
||||||
|
result = BeautifulSoup()
|
||||||
|
rtc = 0
|
||||||
|
open_pTag = False
|
||||||
|
|
||||||
|
all_tokens = list(soup.contents)
|
||||||
|
for token in all_tokens:
|
||||||
|
if type(token) is NavigableString:
|
||||||
|
if not open_pTag:
|
||||||
|
pTag = Tag(result,'p')
|
||||||
|
open_pTag = True
|
||||||
|
ptc = 0
|
||||||
|
pTag.insert(ptc,prepare_string_for_xml(token))
|
||||||
ptc += 1
|
ptc += 1
|
||||||
return soup.renderContents(encoding=None)
|
|
||||||
|
elif token.name in ['br','b','i']:
|
||||||
|
if not open_pTag:
|
||||||
|
pTag = Tag(result,'p')
|
||||||
|
open_pTag = True
|
||||||
|
ptc = 0
|
||||||
|
pTag.insert(ptc, token)
|
||||||
|
ptc += 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
if open_pTag:
|
||||||
|
result.insert(rtc, pTag)
|
||||||
|
rtc += 1
|
||||||
|
open_pTag = False
|
||||||
|
ptc = 0
|
||||||
|
# Clean up NavigableStrings for xml
|
||||||
|
sub_tokens = list(token.contents)
|
||||||
|
sub_soup = BeautifulSoup()
|
||||||
|
for sub_token in sub_tokens:
|
||||||
|
if type(sub_token) is NavigableString:
|
||||||
|
sub_token.replaceWith(prepare_string_for_xml(sub_token))
|
||||||
|
result.insert(rtc, token)
|
||||||
|
rtc += 1
|
||||||
|
|
||||||
|
if open_pTag:
|
||||||
|
result.insert(rtc, pTag)
|
||||||
|
|
||||||
|
paras = result.findAll('p')
|
||||||
|
for p in paras:
|
||||||
|
p['class'] = 'description'
|
||||||
|
|
||||||
|
return result.renderContents(encoding=None)
|
||||||
|
|
||||||
def processSpecialTags(self, tags, this_title, opts):
|
def processSpecialTags(self, tags, this_title, opts):
|
||||||
tag_list = []
|
tag_list = []
|
||||||
@ -2847,6 +2991,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
opts.basename = "Catalog"
|
opts.basename = "Catalog"
|
||||||
opts.plugin_path = self.plugin_path
|
opts.plugin_path = self.plugin_path
|
||||||
opts.cli_environment = not hasattr(opts,'sync')
|
opts.cli_environment = not hasattr(opts,'sync')
|
||||||
|
# GwR *** hardwired for the moment
|
||||||
|
opts.sort_descriptions_by_author = True
|
||||||
|
|
||||||
if opts.verbose:
|
if opts.verbose:
|
||||||
opts_dict = vars(opts)
|
opts_dict = vars(opts)
|
||||||
@ -2863,7 +3009,7 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
for key in keys:
|
for key in keys:
|
||||||
if key in ['catalog_title','exclude_genre','exclude_tags','generate_titles',
|
if key in ['catalog_title','exclude_genre','exclude_tags','generate_titles',
|
||||||
'generate_recently_added','note_tag','numbers_as_text','read_tag',
|
'generate_recently_added','note_tag','numbers_as_text','read_tag',
|
||||||
'search_text','sort_by','sync']:
|
'search_text','sort_by','sort_descriptions_by_author','sync']:
|
||||||
log(" %s: %s" % (key, opts_dict[key]))
|
log(" %s: %s" % (key, opts_dict[key]))
|
||||||
|
|
||||||
# Launch the Catalog builder
|
# Launch the Catalog builder
|
||||||
|
Loading…
x
Reference in New Issue
Block a user