Catalog generation: Fixed improper title display in catalog when title contains ':'. Added 'ondevice' field to CSV/XML catalog output (only with connected device|folder|iTunes). Added optional 'Series' section to generated catalogs with hyperlinks between books and series. Tweaks to catalog formatting

This commit is contained in:
Kovid Goyal 2010-09-08 09:37:42 -06:00
commit 336db024ae
10 changed files with 807 additions and 440 deletions

View File

@ -6,7 +6,7 @@ p.title {
text-align:center; text-align:center;
font-style:italic; font-style:italic;
font-size:xx-large; font-size:xx-large;
border-bottom: solid black 4px; border-bottom: solid black 2px;
} }
p.author { p.author {
@ -17,6 +17,15 @@ p.author {
font-size:large; font-size:large;
} }
p.author_index {
font-size:large;
font-weight:bold;
text-align:left;
margin-top:0px;
margin-bottom:-2px;
text-indent: 0em;
}
p.tags { p.tags {
margin-top:0em; margin-top:0em;
margin-bottom:0em; margin-bottom:0em;
@ -47,19 +56,12 @@ p.letter_index {
margin-bottom:0px; margin-bottom:0px;
} }
p.author_index {
font-size:large;
text-align:left;
margin-top:0px;
margin-bottom:0px;
text-indent: 0em;
}
p.series { p.series {
text-align: left; font-style:italic;
margin-top:0px; margin-top:2px;
margin-bottom:0px; margin-bottom:0px;
margin-left:2em; margin-left:2em;
text-align:left;
text-indent:-2em; text-indent:-2em;
} }
@ -87,11 +89,13 @@ p.date_read {
text-indent:-6em; text-indent:-6em;
} }
hr.series_divider { hr.description_divider {
width:50%; width:90%;
margin-left:1em; margin-left:5%;
margin-top:0em; border-top: solid white 0px;
margin-bottom:0em; border-right: solid white 0px;
border-bottom: solid black 1px;
border-left: solid white 0px;
} }
hr.annotations_divider { hr.annotations_divider {

View File

@ -294,7 +294,7 @@ class CatalogPlugin(Plugin): # {{{
# Return a list of requested fields, with opts.sort_by first # Return a list of requested fields, with opts.sort_by first
all_fields = set( all_fields = set(
['author_sort','authors','comments','cover','formats', ['author_sort','authors','comments','cover','formats',
'id','isbn','pubdate','publisher','rating', 'id','isbn','ondevice','pubdate','publisher','rating',
'series_index','series','size','tags','timestamp', 'series_index','series','size','tags','timestamp',
'title','uuid']) 'title','uuid'])
@ -306,6 +306,9 @@ class CatalogPlugin(Plugin): # {{{
else: else:
fields = list(all_fields) fields = list(all_fields)
if not opts.connected_device['is_device_connected'] and 'ondevice' in fields:
fields.pop(int(fields.index('ondevice')))
fields.sort() fields.sort()
if opts.sort_by and opts.sort_by in fields: if opts.sort_by and opts.sort_by in fields:
fields.insert(0,fields.pop(int(fields.index(opts.sort_by)))) fields.insert(0,fields.pop(int(fields.index(opts.sort_by))))

View File

@ -2303,9 +2303,9 @@ class ITUNES(DriverBase):
# Delete existing from Device|Books, add to self.update_list # Delete existing from Device|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata # for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid and \ if self.cached_books[book]['uuid'] == metadata.uuid or \
self.cached_books[book]['title'] == metadata.title and \ (self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]: self.cached_books[book]['author'] == metadata.authors[0]):
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_device(self.cached_books[book]) self._remove_from_device(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2322,9 +2322,9 @@ class ITUNES(DriverBase):
# Delete existing from Library|Books, add to self.update_list # Delete existing from Library|Books, add to self.update_list
# for deletion from booklist[0] during add_books_to_metadata # for deletion from booklist[0] during add_books_to_metadata
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid and \ if self.cached_books[book]['uuid'] == metadata.uuid or \
self.cached_books[book]['title'] == metadata.title and \ (self.cached_books[book]['title'] == metadata.title and \
self.cached_books[book]['author'] == metadata.authors[0]: self.cached_books[book]['author'] == metadata.authors[0]):
self.update_list.append(self.cached_books[book]) self.update_list.append(self.cached_books[book])
self._remove_from_iTunes(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2488,7 +2488,7 @@ class ITUNES(DriverBase):
zf_opf.close() zf_opf.close()
# If 'News' in tags, tweak the title/author for friendlier display in iBooks # If 'News' in tags, tweak the title/author for friendlier display in iBooks
if _('News') in metadata.tags: if _('News') or _('Catalog') in metadata.tags:
if metadata.title.find('[') > 0: if metadata.title.find('[') > 0:
metadata.title = metadata.title[:metadata.title.find('[')-1] metadata.title = metadata.title[:metadata.title.find('[')-1]
date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))

View File

@ -26,14 +26,18 @@ class GenerateCatalogAction(InterfaceAction):
rows = xrange(self.gui.library_view.model().rowCount(QModelIndex())) rows = xrange(self.gui.library_view.model().rowCount(QModelIndex()))
ids = map(self.gui.library_view.model().id, rows) ids = map(self.gui.library_view.model().id, rows)
dbspec = None
if not ids: if not ids:
return error_dialog(self.gui, _('No books selected'), return error_dialog(self.gui, _('No books selected'),
_('No books selected to generate catalog for'), _('No books selected to generate catalog for'),
show=True) show=True)
db = self.gui.library_view.model().db
dbspec = {}
for id in ids:
dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)}
# Calling gui2.tools:generate_catalog() # Calling gui2.tools:generate_catalog()
ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager.device) ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager)
if ret is None: if ret is None:
return return

View File

@ -19,6 +19,7 @@ class PluginWidget(QWidget,Ui_Form):
OPTION_FIELDS = [('exclude_genre','\[.+\]'), OPTION_FIELDS = [('exclude_genre','\[.+\]'),
('exclude_tags','~,'+_('Catalog')), ('exclude_tags','~,'+_('Catalog')),
('generate_titles', True), ('generate_titles', True),
('generate_series', True),
('generate_recently_added', True), ('generate_recently_added', True),
('note_tag','*'), ('note_tag','*'),
('numbers_as_text', False), ('numbers_as_text', False),
@ -40,7 +41,7 @@ class PluginWidget(QWidget,Ui_Form):
# Update dialog fields from stored options # Update dialog fields from stored options
for opt in self.OPTION_FIELDS: for opt in self.OPTION_FIELDS:
opt_value = gprefs.get(self.name + '_' + opt[0], opt[1]) opt_value = gprefs.get(self.name + '_' + opt[0], opt[1])
if opt[0] in ['numbers_as_text','generate_titles','generate_recently_added']: if opt[0] in ['numbers_as_text','generate_titles','generate_series','generate_recently_added']:
getattr(self, opt[0]).setChecked(opt_value) getattr(self, opt[0]).setChecked(opt_value)
else: else:
getattr(self, opt[0]).setText(opt_value) getattr(self, opt[0]).setText(opt_value)
@ -52,13 +53,13 @@ class PluginWidget(QWidget,Ui_Form):
# others store as lists # others store as lists
opts_dict = {} opts_dict = {}
for opt in self.OPTION_FIELDS: for opt in self.OPTION_FIELDS:
if opt[0] in ['numbers_as_text','generate_titles','generate_recently_added']: if opt[0] in ['numbers_as_text','generate_titles','generate_series','generate_recently_added']:
opt_value = getattr(self,opt[0]).isChecked() opt_value = getattr(self,opt[0]).isChecked()
else: else:
opt_value = unicode(getattr(self, opt[0]).text()) opt_value = unicode(getattr(self, opt[0]).text())
gprefs.set(self.name + '_' + opt[0], opt_value) gprefs.set(self.name + '_' + opt[0], opt_value)
if opt[0] in ['exclude_genre','numbers_as_text','generate_titles','generate_recently_added']: if opt[0] in ['exclude_genre','numbers_as_text','generate_titles','generate_series','generate_recently_added']:
opts_dict[opt[0]] = opt_value opts_dict[opt[0]] = opt_value
else: else:
opts_dict[opt[0]] = opt_value.split(',') opts_dict[opt[0]] = opt_value.split(',')

View File

@ -108,20 +108,27 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QCheckBox" name="generate_recently_added"> <widget class="QCheckBox" name="generate_recently_added">
<property name="text"> <property name="text">
<string>Include 'Recently Added' Section</string> <string>Include 'Recently Added' Section</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="11" column="0">
<widget class="QCheckBox" name="numbers_as_text"> <widget class="QCheckBox" name="numbers_as_text">
<property name="text"> <property name="text">
<string>Sort numbers as text</string> <string>Sort numbers as text</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="0">
<widget class="QCheckBox" name="generate_series">
<property name="text">
<string>Include 'Series' Section</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -29,6 +29,7 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne
log = Log() log = Log()
from calibre.library import db from calibre.library import db
db = db() db = db()
db.catalog_plugin_on_device_temp_mapping = dbspec
# Create a minimal OptionParser that we can append to # Create a minimal OptionParser that we can append to
parser = OptionParser() parser = OptionParser()

View File

@ -238,7 +238,7 @@ def fetch_scheduled_recipe(arg):
return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt] return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt]
def generate_catalog(parent, dbspec, ids, device): def generate_catalog(parent, dbspec, ids, device_manager):
from calibre.gui2.dialogs.catalog import Catalog from calibre.gui2.dialogs.catalog import Catalog
# Build the Catalog dialog in gui2.dialogs.catalog # Build the Catalog dialog in gui2.dialogs.catalog
@ -252,9 +252,18 @@ def generate_catalog(parent, dbspec, ids, device):
# Profile the connected device # Profile the connected device
# Parallel initialization in calibre.library.cli:command_catalog() # Parallel initialization in calibre.library.cli:command_catalog()
connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None} connected_device = {
'is_device_connected': device_manager.is_device_connected,
'kind': device_manager.connected_device_kind,
'name': None,
'save_template': None,
'serial': None,
'storage': None
}
if device: if device_manager.is_device_connected:
device = device_manager.device
connected_device['name'] = device.gui_name
try: try:
storage = [] storage = []
if device._main_prefix: if device._main_prefix:
@ -263,11 +272,10 @@ def generate_catalog(parent, dbspec, ids, device):
storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A)) storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A))
if device._card_b_prefix: if device._card_b_prefix:
storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B)) storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B))
connected_device = { 'storage': storage, connected_device['storage'] = storage
'serial': device.detected_device.serial if \ connected_device['serial'] = device.detected_device.serial if \
hasattr(device.detected_device,'serial') else None, hasattr(device.detected_device,'serial') else None
'save_template': device.save_template(), connected_device['save_template'] = device.save_template()
'name': device.gui_name}
except: except:
pass pass

View File

@ -20,7 +20,7 @@ from calibre.utils.date import isoformat, now as nowf
from calibre.utils.logging import default_log as log from calibre.utils.logging import default_log as log
FIELDS = ['all', 'author_sort', 'authors', 'comments', FIELDS = ['all', 'author_sort', 'authors', 'comments',
'cover', 'formats', 'id', 'isbn', 'pubdate', 'publisher', 'rating', 'cover', 'formats', 'id', 'isbn', 'ondevice', 'pubdate', 'publisher', 'rating',
'series_index', 'series', 'size', 'tags', 'timestamp', 'title', 'series_index', 'series', 'size', 'tags', 'timestamp', 'title',
'uuid'] 'uuid']
@ -67,6 +67,8 @@ class CSV_XML(CatalogPlugin):
if opts.verbose: if opts.verbose:
opts_dict = vars(opts) opts_dict = vars(opts)
log("%s(): Generating %s" % (self.name,self.fmt)) log("%s(): Generating %s" % (self.name,self.fmt))
if opts.connected_device['is_device_connected']:
log(" connected_device: %s" % opts.connected_device['name'])
if opts_dict['search_text']: if opts_dict['search_text']:
log(" --search='%s'" % opts_dict['search_text']) log(" --search='%s'" % opts_dict['search_text'])
@ -81,7 +83,6 @@ class CSV_XML(CatalogPlugin):
else: else:
log(" Fields: %s" % opts_dict['fields']) log(" Fields: %s" % opts_dict['fields'])
# If a list of ids are provided, don't use search_text # If a list of ids are provided, don't use search_text
if opts.ids: if opts.ids:
opts.search_text = None opts.search_text = None
@ -95,6 +96,11 @@ class CSV_XML(CatalogPlugin):
# Get the requested output fields as a list # Get the requested output fields as a list
fields = self.get_output_fields(opts) fields = self.get_output_fields(opts)
# If connected device, add 'On Device' values to data
if opts.connected_device['is_device_connected'] and 'ondevice' in fields:
for entry in data:
entry['ondevice'] = db.catalog_plugin_on_device_temp_mapping[entry['id']]['ondevice']
if self.fmt == 'csv': if self.fmt == 'csv':
outfile = codecs.open(path_to_output, 'w', 'utf8') outfile = codecs.open(path_to_output, 'w', 'utf8')
@ -140,10 +146,10 @@ class CSV_XML(CatalogPlugin):
root.append(record) root.append(record)
for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size', for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size',
'isbn'): 'isbn','ondevice'):
if field in fields: if field in fields:
val = r[field] val = r[field]
if val is None: if not val:
continue continue
if not isinstance(val, (str, unicode)): if not isinstance(val, (str, unicode)):
val = unicode(val) val = unicode(val)
@ -561,6 +567,13 @@ class EPUB_MOBI(CatalogPlugin):
help=_("Include 'Titles' section in catalog.\n" help=_("Include 'Titles' section in catalog.\n"
"Default: '%default'\n" "Default: '%default'\n"
"Applies to: ePub, MOBI output formats")), "Applies to: ePub, MOBI output formats")),
Option('--generate-series',
default=False,
dest='generate_series',
action = 'store_true',
help=_("Include 'Series' section in catalog.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--generate-recently-added', Option('--generate-recently-added',
default=False, default=False,
dest='generate_recently_added', dest='generate_recently_added',
@ -886,8 +899,12 @@ class EPUB_MOBI(CatalogPlugin):
self.__totalSteps += 2 self.__totalSteps += 2
if self.generateRecentlyRead: if self.generateRecentlyRead:
self.__totalSteps += 2 self.__totalSteps += 2
if self.opts.generate_series:
self.__totalSteps += 2
# Accessors # Accessors
if True:
''' '''
@dynamic_property @dynamic_property
def xxxx(self): def xxxx(self):
@ -897,7 +914,6 @@ class EPUB_MOBI(CatalogPlugin):
self.__ = val self.__ = val
return property(fget=fget, fset=fset) return property(fget=fget, fset=fset)
''' '''
@dynamic_property @dynamic_property
def authorClip(self): def authorClip(self):
def fget(self): def fget(self):
@ -1196,6 +1212,8 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByAuthor() self.generateHTMLByAuthor()
if self.opts.generate_titles: if self.opts.generate_titles:
self.generateHTMLByTitle() self.generateHTMLByTitle()
if self.opts.generate_series:
self.generateHTMLBySeries()
if self.opts.generate_recently_added: if self.opts.generate_recently_added:
self.generateHTMLByDateAdded() self.generateHTMLByDateAdded()
if self.generateRecentlyRead: if self.generateRecentlyRead:
@ -1206,15 +1224,17 @@ class EPUB_MOBI(CatalogPlugin):
self.generateOPF() self.generateOPF()
self.generateNCXHeader() self.generateNCXHeader()
self.generateNCXDescriptions("Descriptions")
self.generateNCXByAuthor("Authors") self.generateNCXByAuthor("Authors")
if self.opts.generate_titles: if self.opts.generate_titles:
self.generateNCXByTitle("Titles") self.generateNCXByTitle("Titles")
if self.opts.generate_series:
self.generateNCXBySeries("Series")
if self.opts.generate_recently_added: if self.opts.generate_recently_added:
self.generateNCXByDateAdded("Recently Added") self.generateNCXByDateAdded("Recently Added")
if self.generateRecentlyRead: if self.generateRecentlyRead:
self.generateNCXByDateRead("Recently Read") self.generateNCXByDateRead("Recently Read")
self.generateNCXByGenre("Genres") self.generateNCXByGenre("Genres")
self.generateNCXDescriptions("Descriptions")
self.writeNCX() self.writeNCX()
return True return True
@ -1569,9 +1589,22 @@ class EPUB_MOBI(CatalogPlugin):
emTag = Tag(soup, "em") emTag = Tag(soup, "em")
if title['series']: if title['series']:
# title<br />series series_index # title<br />series series_index
if self.opts.generate_series:
brTag = Tag(soup,'br') brTag = Tag(soup,'br')
title_tokens = title['title'].split(': ') title_tokens = list(title['title'].partition(':'))
emTag.insert(0, escape(NavigableString(title_tokens[1]))) emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
emTag.insert(1, brTag)
smallTag = Tag(soup,'small')
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',title['series']).lower())
aTag.insert(0, title_tokens[0])
smallTag.insert(0, aTag)
emTag.insert(2, smallTag)
else:
brTag = Tag(soup,'br')
title_tokens = list(title['title'].partition(':'))
emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
emTag.insert(1, brTag) emTag.insert(1, brTag)
smallTag = Tag(soup,'small') smallTag = Tag(soup,'small')
smallTag.insert(0, escape(NavigableString(title_tokens[0]))) smallTag.insert(0, escape(NavigableString(title_tokens[0])))
@ -1724,17 +1757,17 @@ class EPUB_MOBI(CatalogPlugin):
body.insert(btc, aTag) body.insert(btc, aTag)
btc += 1 btc += 1
''' if not self.__generateForKindle:
# We don't need this because the Kindle shows section titles # We don't need this because the Kindle shows section titles
#<h2><a name="byalphatitle" id="byalphatitle"></a>By Title</h2> #<h2><a name="byalphatitle" id="byalphatitle"></a>By Title</h2>
h2Tag = Tag(soup, "h2") pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['name'] = "bytitle" aTag['name'] = "bytitle"
h2Tag.insert(0,aTag) pTag.insert(0,aTag)
h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle))) pTag.insert(1,NavigableString('Titles'))
body.insert(btc,h2Tag) body.insert(btc,pTag)
btc += 1 btc += 1
'''
# <p class="letter_index"> # <p class="letter_index">
# <p class="book_title"> # <p class="book_title">
@ -1742,13 +1775,13 @@ class EPUB_MOBI(CatalogPlugin):
dtc = 0 dtc = 0
current_letter = "" current_letter = ""
# 2/14/10 7:11 AM Experimental: re-sort title list without leading series/series_index # Re-sort title list without leading series/series_index
if not self.useSeriesPrefixInTitlesSection: if not self.useSeriesPrefixInTitlesSection:
nspt = deepcopy(self.booksByTitle) nspt = deepcopy(self.booksByTitle)
for book in nspt: for book in nspt:
if book['series']: if book['series']:
tokens = book['title'].split(': ') tokens = book['title'].partition(':')
book['title'] = '%s (%s)' % (tokens[1], tokens[0]) book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0])
book['title_sort'] = self.generateSortTitle(book['title']) book['title_sort'] = self.generateSortTitle(book['title'])
nspt = sorted(nspt, nspt = sorted(nspt,
key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper())) key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper()))
@ -1835,7 +1868,7 @@ class EPUB_MOBI(CatalogPlugin):
# Write books by author A-Z # Write books by author A-Z
self.updateProgressFullStep("'Authors'") self.updateProgressFullStep("'Authors'")
friendly_name = "By Author" friendly_name = "Authors"
soup = self.generateHTMLEmptyHeader(friendly_name) soup = self.generateHTMLEmptyHeader(friendly_name)
body = soup.find('body') body = soup.find('body')
@ -1906,15 +1939,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None current_series = None
pAuthorTag = Tag(soup, "p") pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index" pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['name'] = "%s" % self.generateAuthorAnchor(current_author) aTag['name'] = "%s" % self.generateAuthorAnchor(current_author)
aTag.insert(0,NavigableString(current_author)) aTag.insert(0,NavigableString(current_author))
emTag.insert(0,aTag) pAuthorTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
divTag.insert(dtc,pAuthorTag) divTag.insert(dtc,pAuthorTag)
dtc += 1 dtc += 1
'''
# Insert an <hr /> between non-series and series # Insert an <hr /> between non-series and series
if not current_series and non_series_books and book['series']: if not current_series and non_series_books and book['series']:
# Insert an <hr /> # Insert an <hr />
@ -1922,6 +1954,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider" hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag) divTag.insert(dtc,hrTag)
dtc += 1 dtc += 1
'''
# Check for series # Check for series
if book['series'] and book['series'] != current_series: if book['series'] and book['series'] != current_series:
@ -1929,7 +1962,18 @@ class EPUB_MOBI(CatalogPlugin):
current_series = book['series'] current_series = book['series']
pSeriesTag = Tag(soup,'p') pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series" pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series']))
if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',book['series']).lower())
aTag.insert(0, book['series'])
#pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL))
pSeriesTag.insert(0, aTag)
else:
#pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
divTag.insert(dtc,pSeriesTag) divTag.insert(dtc,pSeriesTag)
dtc += 1 dtc += 1
if current_series and not book['series']: if current_series and not book['series']:
@ -1957,30 +2001,34 @@ 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 # Use series, series index if avail else just title, + year of publication
if current_series: if current_series:
aTag.insert(0,escape(book['title'][len(book['series'])+1:])) aTag.insert(0,'%s (%s)' % (escape(book['title'][len(book['series'])+1:]),
book['date'].split()[1]))
else: else:
aTag.insert(0,escape(book['title'])) aTag.insert(0,'%s (%s)' % (escape(book['title']),
book['date'].split()[1]))
non_series_books += 1 non_series_books += 1
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
divTag.insert(dtc, pBookTag) divTag.insert(dtc, pBookTag)
dtc += 1 dtc += 1
''' if not self.__generateForKindle:
# Insert the <h2> tag with book_count at the head # Insert the <h2> tag with book_count at the head
#<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2> #<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2>
h2Tag = Tag(soup, "h2") pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
anchor_name = friendly_name.lower() anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","") aTag['name'] = anchor_name.replace(" ","")
h2Tag.insert(0,aTag) pTag.insert(0,aTag)
h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count))) #h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count)))
body.insert(btc,h2Tag) pTag.insert(1,NavigableString('%s' % (friendly_name)))
body.insert(btc,pTag)
btc += 1 btc += 1
'''
# Add the divTag to the body # Add the divTag to the body
body.insert(btc, divTag) body.insert(btc, divTag)
@ -2023,15 +2071,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None current_series = None
pAuthorTag = Tag(soup, "p") pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index" pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['name'] = "%s" % self.generateAuthorAnchor(current_author) aTag['name'] = "%s" % self.generateAuthorAnchor(current_author)
aTag.insert(0,NavigableString(current_author)) aTag.insert(0,NavigableString(current_author))
emTag.insert(0,aTag) pAuthorTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
divTag.insert(dtc,pAuthorTag) divTag.insert(dtc,pAuthorTag)
dtc += 1 dtc += 1
'''
# Insert an <hr /> between non-series and series # Insert an <hr /> between non-series and series
if not current_series and non_series_books and new_entry['series']: if not current_series and non_series_books and new_entry['series']:
# Insert an <hr /> # Insert an <hr />
@ -2039,6 +2086,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider" hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag) divTag.insert(dtc,hrTag)
dtc += 1 dtc += 1
'''
# Check for series # Check for series
if new_entry['series'] and new_entry['series'] != current_series: if new_entry['series'] and new_entry['series'] != current_series:
@ -2046,7 +2094,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = new_entry['series'] current_series = new_entry['series']
pSeriesTag = Tag(soup,'p') pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series" pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + new_entry['series'])) if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',new_entry['series']).lower())
aTag.insert(0, new_entry['series'])
pSeriesTag.insert(0, aTag)
else:
pSeriesTag.insert(0,NavigableString('%s' % new_entry['series']))
divTag.insert(dtc,pSeriesTag) divTag.insert(dtc,pSeriesTag)
dtc += 1 dtc += 1
if current_series and not new_entry['series']: if current_series and not new_entry['series']:
@ -2160,18 +2215,18 @@ class EPUB_MOBI(CatalogPlugin):
aTag['name'] = anchor_name.replace(" ","") aTag['name'] = anchor_name.replace(" ","")
body.insert(btc, aTag) body.insert(btc, aTag)
btc += 1 btc += 1
'''
# We don't need this because the kindle inserts section titles if not self.__generateForKindle:
#<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2> #<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2>
h2Tag = Tag(soup, "h2") pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
anchor_name = friendly_name.lower() anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","") aTag['name'] = anchor_name.replace(" ","")
h2Tag.insert(0,aTag) pTag.insert(0,aTag)
h2Tag.insert(1,NavigableString('%s' % friendly_name)) pTag.insert(1,NavigableString('%s' % friendly_name))
body.insert(btc,h2Tag) body.insert(btc,pTag)
btc += 1 btc += 1
'''
# <p class="letter_index"> # <p class="letter_index">
# <p class="author_index"> # <p class="author_index">
@ -2186,14 +2241,13 @@ class EPUB_MOBI(CatalogPlugin):
nspt = deepcopy(self.booksByTitle) nspt = deepcopy(self.booksByTitle)
for book in nspt: for book in nspt:
if book['series']: if book['series']:
tokens = book['title'].split(': ') tokens = book['title'].partition(':')
book['title'] = '%s (%s)' % (tokens[1], tokens[0]) book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0])
book['title_sort'] = self.generateSortTitle(book['title']) book['title_sort'] = self.generateSortTitle(book['title'])
self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True) self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True)
date_range_list = [] date_range_list = []
today_time = nowf().replace(hour=23, minute=59, second=59) today_time = nowf().replace(hour=23, minute=59, second=59)
books_added_in_date_range = False
for (i, date) in enumerate(self.DATE_RANGE): for (i, date) in enumerate(self.DATE_RANGE):
date_range_limit = self.DATE_RANGE[i] date_range_limit = self.DATE_RANGE[i]
if i: if i:
@ -2206,18 +2260,20 @@ class EPUB_MOBI(CatalogPlugin):
delta = today_time-book_time delta = today_time-book_time
if delta.days <= date_range_limit: if delta.days <= date_range_limit:
date_range_list.append(book) date_range_list.append(book)
books_added_in_date_range = True
else: else:
break break
dtc = add_books_to_HTML_by_date_range(date_range_list, date_range, dtc) dtc = add_books_to_HTML_by_date_range(date_range_list, date_range, dtc)
date_range_list = [book] date_range_list = [book]
'''
if books_added_in_date_range: if books_added_in_date_range:
# Add an <hr> separating date ranges from months # Add an <hr> separating date ranges from months
hrTag = Tag(soup,'hr') hrTag = Tag(soup,'hr')
hrTag['class'] = "description_divider"
divTag.insert(dtc,hrTag) divTag.insert(dtc,hrTag)
dtc += 1 dtc += 1
'''
# >>>> Books by month <<<< # >>>> Books by month <<<<
# Sort titles case-insensitive for by month using series prefix # Sort titles case-insensitive for by month using series prefix
@ -2437,6 +2493,174 @@ class EPUB_MOBI(CatalogPlugin):
outfile.close() outfile.close()
self.htmlFileList.append("content/ByDateRead.html") self.htmlFileList.append("content/ByDateRead.html")
def generateHTMLBySeries(self):
'''
Generate a list of series
'''
self.updateProgressFullStep("Fetching series")
self.opts.sort_by = 'series'
# Merge opts.exclude_tags with opts.search_text
# Updated to use exact match syntax
empty_exclude_tags = False if len(self.opts.exclude_tags) else True
search_phrase = 'series:true '
if not empty_exclude_tags:
exclude_tags = self.opts.exclude_tags.split(',')
search_terms = []
for tag in exclude_tags:
search_terms.append("tag:=%s" % tag)
search_phrase += "not (%s)" % " or ".join(search_terms)
# If a list of ids are provided, don't use search_text
if self.opts.ids:
self.opts.search_text = search_phrase
else:
if self.opts.search_text:
self.opts.search_text += " " + search_phrase
else:
self.opts.search_text = search_phrase
# Fetch the database as a dictionary
self.booksBySeries = self.plugin.search_sort_db(self.db, self.opts)
friendly_name = "Series"
soup = self.generateHTMLEmptyHeader(friendly_name)
body = soup.find('body')
btc = 0
# Insert section tag
aTag = Tag(soup,'a')
aTag['name'] = 'section_start'
body.insert(btc, aTag)
btc += 1
# Insert the anchor
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
body.insert(btc, aTag)
btc += 1
# <p class="letter_index">
# <p class="author_index">
divTag = Tag(soup, "div")
dtc = 0
current_letter = ""
current_series = None
# Loop through booksBySeries
series_count = 0
for book in self.booksBySeries:
# Check for initial letter change
sort_title = self.generateSortTitle(book['series'])
if self.letter_or_symbol(sort_title[0].upper()) != current_letter :
'''
# Start a new letter - anchor only, hidden
current_letter = book['author_sort'][0].upper()
aTag = Tag(soup, "a")
aTag['name'] = "%sseries" % current_letter
divTag.insert(dtc, aTag)
dtc += 1
'''
# Start a new letter with Index letter
current_letter = self.letter_or_symbol(sort_title[0].upper())
pIndexTag = Tag(soup, "p")
pIndexTag['class'] = "letter_index"
aTag = Tag(soup, "a")
aTag['name'] = "%s_series" % self.letter_or_symbol(current_letter)
pIndexTag.insert(0,aTag)
pIndexTag.insert(1,NavigableString(self.letter_or_symbol(sort_title[0].upper())))
divTag.insert(dtc,pIndexTag)
dtc += 1
# Check for series change
if book['series'] != current_series:
# Start a new series
series_count += 1
current_series = book['series']
pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series"
aTag = Tag(soup, 'a')
aTag['name'] = "%s_series" % re.sub('\W','',book['series']).lower()
pSeriesTag.insert(0,aTag)
pSeriesTag.insert(1,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
divTag.insert(dtc,pSeriesTag)
dtc += 1
# Add books
pBookTag = Tag(soup, "p")
ptc = 0
# book with read/reading/unread symbol
if 'read' in book and book['read']:
# check mark
pBookTag.insert(ptc,NavigableString(self.READ_SYMBOL))
pBookTag['class'] = "read_book"
ptc += 1
elif book['id'] in self.bookmarked_books:
pBookTag.insert(ptc,NavigableString(self.READING_SYMBOL))
pBookTag['class'] = "read_book"
ptc += 1
else:
# hidden check mark
pBookTag['class'] = "unread_book"
pBookTag.insert(ptc,NavigableString(self.NOT_READ_SYMBOL))
ptc += 1
aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
# Use series, series index if avail else just title
#aTag.insert(0,'%d. %s &middot; %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors'])))
# Link to book
aTag.insert(0,'%d. %s (%s)' % (book['series_index'],
escape(book['title']),
strftime(u'%Y', book['pubdate'].timetuple())))
pBookTag.insert(ptc, aTag)
ptc += 1
# &middot;
pBookTag.insert(ptc, NavigableString(' &middot; '))
ptc += 1
# Link to author
aTag = Tag(soup, "a")
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor",
self.generateAuthorAnchor(escape(' & '.join(book['authors']))))
aTag.insert(0, NavigableString(' &amp; '.join(book['authors'])))
pBookTag.insert(ptc, aTag)
ptc += 1
divTag.insert(dtc, pBookTag)
dtc += 1
if not self.__generateForKindle:
# Insert the <h2> tag with book_count at the head
#<h2><a name="byseries" id="byseries"></a>By Series</h2>
pTag = Tag(soup, "p")
pTag['class'] = 'title'
aTag = Tag(soup, "a")
anchor_name = friendly_name.lower()
aTag['name'] = anchor_name.replace(" ","")
pTag.insert(0,aTag)
#h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, series_count)))
pTag.insert(1,NavigableString('%s' % friendly_name))
body.insert(btc,pTag)
btc += 1
# Add the divTag to the body
body.insert(btc, divTag)
# Write the generated file to contentdir
outfile_spec = "%s/BySeries.html" % (self.contentDir)
outfile = open(outfile_spec, 'w')
outfile.write(soup.prettify())
outfile.close()
self.htmlFileList.append("content/BySeries.html")
def generateHTMLByTags(self): def generateHTMLByTags(self):
# Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ... # Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ...
# Note that special tags - ~+*[] - have already been filtered from books[] # Note that special tags - ~+*[] - have already been filtered from books[]
@ -2683,22 +2907,7 @@ class EPUB_MOBI(CatalogPlugin):
# HTML files - add books to manifest and spine # HTML files - add books to manifest and spine
sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \ sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \
else self.booksByTitle else self.booksByTitle
for book in sort_descriptions_by: # Add html_files to manifest and spine
# manifest
itemTag = Tag(soup, "item")
itemTag['href'] = "content/book_%d.html" % int(book['id'])
itemTag['id'] = "book%d" % int(book['id'])
itemTag['media-type'] = "application/xhtml+xml"
manifest.insert(mtc, itemTag)
mtc += 1
# spine
itemrefTag = Tag(soup, "itemref")
itemrefTag['idref'] = "book%d" % int(book['id'])
spine.insert(stc, itemrefTag)
stc += 1
# Add other html_files to manifest and spine
for file in self.htmlFileList: for file in self.htmlFileList:
itemTag = Tag(soup, "item") itemTag = Tag(soup, "item")
@ -2734,6 +2943,21 @@ class EPUB_MOBI(CatalogPlugin):
spine.insert(stc, itemrefTag) spine.insert(stc, itemrefTag)
stc += 1 stc += 1
for book in sort_descriptions_by:
# manifest
itemTag = Tag(soup, "item")
itemTag['href'] = "content/book_%d.html" % int(book['id'])
itemTag['id'] = "book%d" % int(book['id'])
itemTag['media-type'] = "application/xhtml+xml"
manifest.insert(mtc, itemTag)
mtc += 1
# spine
itemrefTag = Tag(soup, "itemref")
itemrefTag['idref'] = "book%d" % int(book['id'])
spine.insert(stc, itemrefTag)
stc += 1
# Guide # Guide
referenceTag = Tag(soup, "reference") referenceTag = Tag(soup, "reference")
referenceTag['type'] = 'masthead' referenceTag['type'] = 'masthead'
@ -2769,7 +2993,8 @@ class EPUB_MOBI(CatalogPlugin):
navLabelTag.insert(0, textTag) navLabelTag.insert(0, textTag)
navPointTag.insert(0, navLabelTag) navPointTag.insert(0, navLabelTag)
contentTag = Tag(soup, 'content') contentTag = Tag(soup, 'content')
contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id']) #contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id'])
contentTag['src'] = "content/ByAlphaAuthor.html"
navPointTag.insert(1, contentTag) navPointTag.insert(1, contentTag)
cmiTag = Tag(soup, '%s' % 'calibre:meta-img') cmiTag = Tag(soup, '%s' % 'calibre:meta-img')
cmiTag['name'] = "mastheadImage" cmiTag['name'] = "mastheadImage"
@ -2821,15 +3046,15 @@ class EPUB_MOBI(CatalogPlugin):
navLabelTag = Tag(ncx_soup, "navLabel") navLabelTag = Tag(ncx_soup, "navLabel")
textTag = Tag(ncx_soup, "text") textTag = Tag(ncx_soup, "text")
if book['series']: if book['series']:
tokens = book['title'].split(': ') tokens = list(book['title'].partition(':'))
if self.generateForKindle: if self.generateForKindle:
# Don't include Author for Kindle # Don't include Author for Kindle
textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % \ textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % \
(tokens[1], tokens[0]), dest='title'))) (tokens[2].strip(), tokens[0]), dest='title')))
else: else:
# Include Author for non-Kindle # Include Author for non-Kindle
textTag.insert(0, NavigableString(self.formatNCXText('%s &middot; %s (%s)' % \ textTag.insert(0, NavigableString(self.formatNCXText('%s &middot; %s (%s)' % \
(tokens[1], book['author'], tokens[0]), dest='title'))) (tokens[2].strip(), book['author'], tokens[0]), dest='title')))
else: else:
if self.generateForKindle: if self.generateForKindle:
# Don't include Author for Kindle # Don't include Author for Kindle
@ -2882,6 +3107,98 @@ class EPUB_MOBI(CatalogPlugin):
self.ncxSoup = ncx_soup self.ncxSoup = ncx_soup
def generateNCXBySeries(self, tocTitle):
self.updateProgressFullStep("NCX 'Series'")
def add_to_series_by_letter(current_series_list):
current_series_list = " &bull; ".join(current_series_list)
current_series_list = self.formatNCXText(current_series_list, dest="description")
series_by_letter.append(current_series_list)
soup = self.ncxSoup
output = "BySeries"
body = soup.find("navPoint")
btc = len(body.contents)
# --- Construct the 'Books By Series' section ---
navPointTag = Tag(soup, 'navPoint')
navPointTag['class'] = "section"
navPointTag['id'] = "byseries-ID"
navPointTag['playOrder'] = self.playOrder
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
textTag.insert(0, NavigableString(tocTitle))
navLabelTag.insert(0, textTag)
nptc = 0
navPointTag.insert(nptc, navLabelTag)
nptc += 1
contentTag = Tag(soup,"content")
contentTag['src'] = "content/%s.html#section_start" % (output)
navPointTag.insert(nptc, contentTag)
nptc += 1
series_by_letter = []
# Loop over the series titles, find start of each letter, add description_preview_count books
# Special switch for using different title list
title_list = self.booksBySeries
current_letter = self.letter_or_symbol(title_list[0]['series'][0])
title_letters = [current_letter]
current_series_list = []
current_series = ""
for book in title_list:
sort_title = self.generateSortTitle(book['series'])
if self.letter_or_symbol(sort_title[0]) != current_letter:
# Save the old list
add_to_series_by_letter(current_series_list)
# Start the new list
current_letter = self.letter_or_symbol(sort_title[0])
title_letters.append(current_letter)
current_series = book['series']
current_series_list = [book['series']]
else:
if len(current_series_list) < self.descriptionClip and \
book['series'] != current_series :
current_series = book['series']
current_series_list.append(book['series'])
# Add the last book list
add_to_series_by_letter(current_series_list)
# Add *article* entries for each populated series title letter
for (i,books) in enumerate(series_by_letter):
navPointByLetterTag = Tag(soup, 'navPoint')
navPointByLetterTag['class'] = "article"
navPointByLetterTag['id'] = "%sSeries-ID" % (title_letters[i].upper())
navPointTag['playOrder'] = self.playOrder
self.playOrder += 1
navLabelTag = Tag(soup, 'navLabel')
textTag = Tag(soup, 'text')
textTag.insert(0, NavigableString(u"Series beginning with %s" % \
(title_letters[i] if len(title_letters[i])>1 else "'" + title_letters[i] + "'")))
navLabelTag.insert(0, textTag)
navPointByLetterTag.insert(0,navLabelTag)
contentTag = Tag(soup, 'content')
contentTag['src'] = "content/%s.html#%s_series" % (output, title_letters[i])
navPointByLetterTag.insert(1,contentTag)
if self.generateForKindle:
cmTag = Tag(soup, '%s' % 'calibre:meta')
cmTag['name'] = "description"
cmTag.insert(0, NavigableString(self.formatNCXText(books, dest='description')))
navPointByLetterTag.insert(2, cmTag)
navPointTag.insert(nptc, navPointByLetterTag)
nptc += 1
# Add this section to the body
body.insert(btc, navPointTag)
btc += 1
self.ncxSoup = soup
def generateNCXByTitle(self, tocTitle): def generateNCXByTitle(self, tocTitle):
self.updateProgressFullStep("NCX 'Titles'") self.updateProgressFullStep("NCX 'Titles'")
@ -3713,7 +4030,7 @@ class EPUB_MOBI(CatalogPlugin):
btc += 1 btc += 1
titleTag = body.find(attrs={'class':'title'}) titleTag = body.find(attrs={'class':'title'})
titleTag.insert(0,NavigableString('<b><i>%s</i></b>' % escape(self.getFriendlyGenreTag(genre)))) titleTag.insert(0,NavigableString('%s' % escape(self.getFriendlyGenreTag(genre))))
# Insert the books by author list # Insert the books by author list
divTag = body.find(attrs={'class':'authors'}) divTag = body.find(attrs={'class':'authors'})
@ -3729,15 +4046,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = None current_series = None
pAuthorTag = Tag(soup, "p") pAuthorTag = Tag(soup, "p")
pAuthorTag['class'] = "author_index" pAuthorTag['class'] = "author_index"
emTag = Tag(soup, "em")
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author'])) aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author']))
aTag.insert(0, book['author']) aTag.insert(0, book['author'])
emTag.insert(0,aTag) pAuthorTag.insert(0,aTag)
pAuthorTag.insert(0,emTag)
divTag.insert(dtc,pAuthorTag) divTag.insert(dtc,pAuthorTag)
dtc += 1 dtc += 1
'''
# Insert an <hr /> between non-series and series # Insert an <hr /> between non-series and series
if not current_series and non_series_books and book['series']: if not current_series and non_series_books and book['series']:
# Insert an <hr /> # Insert an <hr />
@ -3745,6 +4061,7 @@ class EPUB_MOBI(CatalogPlugin):
hrTag['class'] = "series_divider" hrTag['class'] = "series_divider"
divTag.insert(dtc,hrTag) divTag.insert(dtc,hrTag)
dtc += 1 dtc += 1
'''
# Check for series # Check for series
if book['series'] and book['series'] != current_series: if book['series'] and book['series'] != current_series:
@ -3752,7 +4069,14 @@ class EPUB_MOBI(CatalogPlugin):
current_series = book['series'] current_series = book['series']
pSeriesTag = Tag(soup,'p') pSeriesTag = Tag(soup,'p')
pSeriesTag['class'] = "series" pSeriesTag['class'] = "series"
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series'])) if self.opts.generate_series:
aTag = Tag(soup,'a')
aTag['href'] = "%s.html#%s_series" % ('BySeries',
re.sub('\W','',book['series']).lower())
aTag.insert(0, book['series'])
pSeriesTag.insert(0, aTag)
else:
pSeriesTag.insert(0,NavigableString('%s' % book['series']))
divTag.insert(dtc,pSeriesTag) divTag.insert(dtc,pSeriesTag)
dtc += 1 dtc += 1
@ -3809,7 +4133,7 @@ class EPUB_MOBI(CatalogPlugin):
def generateHTMLDescriptionHeader(self, title): def generateHTMLDescriptionHeader(self, title):
title_border = '' if self.opts.fmt == 'epub' else \ title_border = '' if self.opts.fmt == 'epub' else \
'<div class="hr"><blockquote><hr/></blockquote></div>' '<hr class="description_divider"/>'
header = ''' header = '''
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata">
@ -3855,7 +4179,7 @@ class EPUB_MOBI(CatalogPlugin):
<td>&nbsp;</td> <td>&nbsp;</td>
</tr> </tr>
</table> </table>
<blockquote><hr/></blockquote> <hr class="description_divider" />
<div class="description"></div> <div class="description"></div>
</body> </body>
</html> </html>
@ -3897,7 +4221,7 @@ class EPUB_MOBI(CatalogPlugin):
</head> </head>
<body> <body>
<p class="title"></p> <p class="title"></p>
<div class="hr"><blockquote><hr/></blockquote></div> <!--div class="hr"><blockquote><hr/></blockquote></div-->
<div class="authors"></div> <div class="authors"></div>
</body> </body>
</html> </html>
@ -4049,6 +4373,12 @@ class EPUB_MOBI(CatalogPlugin):
except: except:
self.opts.log.error("generateThumbnail(): Error with %s" % title['title']) self.opts.log.error("generateThumbnail(): Error with %s" % title['title'])
def getFriendlyGenreTag(self, genre):
# Find the first instance of friendly_tag matching genre
for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[friendly_tag] == genre:
return friendly_tag
def getMarkerTags(self): def getMarkerTags(self):
''' Return a list of special marker tags to be excluded from genre list ''' ''' Return a list of special marker tags to be excluded from genre list '''
markerTags = [] markerTags = []
@ -4063,12 +4393,6 @@ class EPUB_MOBI(CatalogPlugin):
else: else:
return char return char
def getFriendlyGenreTag(self, genre):
# Find the first instance of friendly_tag matching genre
for friendly_tag in self.genre_tags_dict:
if self.genre_tags_dict[friendly_tag] == genre:
return friendly_tag
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
@ -4224,13 +4548,15 @@ class EPUB_MOBI(CatalogPlugin):
opts.fmt = self.fmt = path_to_output.rpartition('.')[2] opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
# Add local options # Add local options
opts.creator = "calibre" opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y'))
opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d'))
opts.connected_kindle = False opts.connected_kindle = False
# Finalize output_profile # Finalize output_profile
op = opts.output_profile op = opts.output_profile
if op is None: if op is None:
op = 'default' op = 'default'
if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower(): if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower():
opts.connected_kindle = True opts.connected_kindle = True
if opts.connected_device['serial'] and opts.connected_device['serial'][:4] in ['B004','B005']: if opts.connected_device['serial'] and opts.connected_device['serial'][:4] in ['B004','B005']:
@ -4256,7 +4582,8 @@ class EPUB_MOBI(CatalogPlugin):
opts.exclude_genre = '\[^.\]' opts.exclude_genre = '\[^.\]'
build_log.append(" converting empty exclude_genre to '\[^.\]'") build_log.append(" converting empty exclude_genre to '\[^.\]'")
if opts.connected_device['name']: if opts.connected_device['is_device_connected'] and \
opts.connected_device['kind'] == 'device':
if opts.connected_device['serial']: if opts.connected_device['serial']:
build_log.append(u" connected_device: '%s' #%s%s " % \ build_log.append(u" connected_device: '%s' #%s%s " % \
(opts.connected_device['name'], (opts.connected_device['name'],
@ -4267,9 +4594,14 @@ class EPUB_MOBI(CatalogPlugin):
build_log.append(u" mount point: %s" % storage) build_log.append(u" mount point: %s" % storage)
else: else:
build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
try:
for storage in opts.connected_device['storage']: for storage in opts.connected_device['storage']:
if storage: if storage:
build_log.append(u" mount point: %s" % storage) build_log.append(u" mount point: %s" % storage)
except:
build_log.append(u" (no mount points)")
else:
build_log.append(u" connected_device: '%s'" % opts.connected_device['name'])
opts_dict = vars(opts) opts_dict = vars(opts)
if opts_dict['ids']: if opts_dict['ids']:

View File

@ -674,7 +674,14 @@ def command_catalog(args, dbpath):
# No support for connected device in CLI environment # No support for connected device in CLI environment
# Parallel initialization in calibre.gui2.tools:generate_catalog() # Parallel initialization in calibre.gui2.tools:generate_catalog()
opts.connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None} opts.connected_device = {
'is_device_connected': False,
'kind': None,
'name': None,
'save_template': None,
'serial': None,
'storage': None,
}
with plugin: with plugin:
plugin.run(args[1], opts, get_db(dbpath, opts)) plugin.run(args[1], opts, get_db(dbpath, opts))