mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
GwR Series Section revisions
This commit is contained in:
commit
a98531a204
@ -294,7 +294,7 @@ class CatalogPlugin(Plugin): # {{{
|
||||
# Return a list of requested fields, with opts.sort_by first
|
||||
all_fields = set(
|
||||
['author_sort','authors','comments','cover','formats',
|
||||
'id','isbn','pubdate','publisher','rating',
|
||||
'id','isbn','ondevice','pubdate','publisher','rating',
|
||||
'series_index','series','size','tags','timestamp',
|
||||
'title','uuid'])
|
||||
|
||||
@ -306,6 +306,9 @@ class CatalogPlugin(Plugin): # {{{
|
||||
else:
|
||||
fields = list(all_fields)
|
||||
|
||||
if not opts.connected_device['is_device_connected'] and 'ondevice' in fields:
|
||||
fields.pop(int(fields.index('ondevice')))
|
||||
|
||||
fields.sort()
|
||||
if opts.sort_by and opts.sort_by in fields:
|
||||
fields.insert(0,fields.pop(int(fields.index(opts.sort_by))))
|
||||
|
@ -35,10 +35,9 @@ class GenerateCatalogAction(InterfaceAction):
|
||||
dbspec = {}
|
||||
for id in ids:
|
||||
dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)}
|
||||
db.catalog_plugin_on_device_temp_mapping = dbspec
|
||||
|
||||
# Calling gui2.tools:generate_catalog()
|
||||
ret = generate_catalog(self.gui, db, ids, self.gui.device_manager)
|
||||
ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager)
|
||||
if ret is None:
|
||||
return
|
||||
|
||||
|
@ -19,6 +19,7 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
OPTION_FIELDS = [('exclude_genre','\[.+\]'),
|
||||
('exclude_tags','~,'+_('Catalog')),
|
||||
('generate_titles', True),
|
||||
('generate_series', True),
|
||||
('generate_recently_added', True),
|
||||
('note_tag','*'),
|
||||
('numbers_as_text', False),
|
||||
@ -40,7 +41,7 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
# Update dialog fields from stored options
|
||||
for opt in self.OPTION_FIELDS:
|
||||
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)
|
||||
else:
|
||||
getattr(self, opt[0]).setText(opt_value)
|
||||
@ -52,13 +53,13 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
# others store as lists
|
||||
opts_dict = {}
|
||||
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()
|
||||
else:
|
||||
opt_value = unicode(getattr(self, opt[0]).text())
|
||||
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
|
||||
else:
|
||||
opts_dict[opt[0]] = opt_value.split(',')
|
||||
|
@ -108,20 +108,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QCheckBox" name="generate_recently_added">
|
||||
<property name="text">
|
||||
<string>Include 'Recently Added' Section</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<item row="11" column="0">
|
||||
<widget class="QCheckBox" name="numbers_as_text">
|
||||
<property name="text">
|
||||
<string>Sort numbers as text</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="generate_series">
|
||||
<property name="text">
|
||||
<string>Include 'Series' Section</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@ -27,13 +27,9 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne
|
||||
notification=DummyReporter(), log=None):
|
||||
if log is None:
|
||||
log = Log()
|
||||
if dbspec is None:
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
dbpath = prefs['library_path']
|
||||
db = LibraryDatabase2(dbpath)
|
||||
else: # To be implemented in the future
|
||||
pass
|
||||
from calibre.library import db
|
||||
db = db()
|
||||
db.catalog_plugin_on_device_temp_mapping = dbspec
|
||||
|
||||
# Create a minimal OptionParser that we can append to
|
||||
parser = OptionParser()
|
||||
|
@ -10,8 +10,8 @@ from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \
|
||||
QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \
|
||||
QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction, \
|
||||
QPushButton, QHBoxLayout
|
||||
QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \
|
||||
QDialogButtonBox, QHBoxLayout
|
||||
|
||||
from calibre.constants import __appname__, __version__, islinux, isosx
|
||||
from calibre.gui2 import gprefs, min_available_height, available_width, \
|
||||
@ -20,6 +20,8 @@ from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin
|
||||
from calibre.customize.ui import preferences_plugins
|
||||
from calibre.utils.ordered_dict import OrderedDict
|
||||
|
||||
ICON_SIZE = 32
|
||||
|
||||
class StatusBar(QStatusBar): # {{{
|
||||
|
||||
def __init__(self, parent=None):
|
||||
@ -42,6 +44,32 @@ class StatusBar(QStatusBar): # {{{
|
||||
|
||||
# }}}
|
||||
|
||||
class BarTitle(QWidget):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self._layout = QHBoxLayout()
|
||||
self.setLayout(self._layout)
|
||||
self._layout.addStretch(10)
|
||||
self.icon = QLabel('')
|
||||
self._layout.addWidget(self.icon)
|
||||
self.title = QLabel('')
|
||||
self.title.setStyleSheet('QLabel { font-weight: bold }')
|
||||
self.title.setAlignment(Qt.AlignLeft | Qt.AlignCenter)
|
||||
self._layout.addWidget(self.title)
|
||||
self._layout.addStretch(10)
|
||||
|
||||
def show_plugin(self, plugin):
|
||||
self.pmap = QPixmap(plugin.icon).scaled(ICON_SIZE, ICON_SIZE,
|
||||
Qt.KeepAspectRatio, Qt.SmoothTransformation)
|
||||
self.icon.setPixmap(self.pmap)
|
||||
self.title.setText(plugin.gui_name)
|
||||
tt = plugin.description
|
||||
self.setStatusTip(tt)
|
||||
tt = textwrap.fill(tt)
|
||||
self.setToolTip(tt)
|
||||
self.setWhatsThis(tt)
|
||||
|
||||
class Category(QWidget): # {{{
|
||||
|
||||
plugin_activated = pyqtSignal(object)
|
||||
@ -88,7 +116,6 @@ class Category(QWidget): # {{{
|
||||
class Browser(QScrollArea): # {{{
|
||||
|
||||
show_plugin = pyqtSignal(object)
|
||||
close_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QScrollArea.__init__(self, parent)
|
||||
@ -119,14 +146,6 @@ class Browser(QScrollArea): # {{{
|
||||
self.container = QWidget(self)
|
||||
self.container.setLayout(self._layout)
|
||||
self.setWidget(self.container)
|
||||
if isosx:
|
||||
self._osxl = QHBoxLayout()
|
||||
self.close_button = QPushButton(_('Close'))
|
||||
self.close_button.clicked.connect(self.close_requested)
|
||||
self._osxl.addStretch(10)
|
||||
self._osxl.addWidget(self.close_button)
|
||||
#self._osxl.addStretch(10)
|
||||
self._layout.addLayout(self._osxl)
|
||||
|
||||
for name, plugins in self.category_map.items():
|
||||
w = Category(name, plugins, self)
|
||||
@ -134,8 +153,6 @@ class Browser(QScrollArea): # {{{
|
||||
self._layout.addWidget(w)
|
||||
w.plugin_activated.connect(self.show_plugin.emit)
|
||||
|
||||
def close_requested(self, *args):
|
||||
self.close_signal.emit()
|
||||
|
||||
# }}}
|
||||
|
||||
@ -177,9 +194,15 @@ class Preferences(QMainWindow):
|
||||
self.setStatusBar(self.status_bar)
|
||||
|
||||
self.stack = QStackedWidget(self)
|
||||
self.setCentralWidget(self.stack)
|
||||
self.cw = QWidget(self)
|
||||
self.cw.setLayout(QVBoxLayout())
|
||||
self.cw.layout().addWidget(self.stack)
|
||||
self.bb = QDialogButtonBox(QDialogButtonBox.Close)
|
||||
self.cw.layout().addWidget(self.bb)
|
||||
self.bb.rejected.connect(self.close, type=Qt.QueuedConnection)
|
||||
self.setCentralWidget(self.cw)
|
||||
self.bb.setVisible(isosx)
|
||||
self.browser = Browser(self)
|
||||
self.browser.close_signal.connect(self.close, type=Qt.QueuedConnection)
|
||||
self.browser.show_plugin.connect(self.show_plugin)
|
||||
self.stack.addWidget(self.browser)
|
||||
self.scroll_area = QScrollArea(self)
|
||||
@ -189,7 +212,7 @@ class Preferences(QMainWindow):
|
||||
self.bar = QToolBar(self)
|
||||
self.addToolBar(self.bar)
|
||||
self.bar.setVisible(False)
|
||||
self.bar.setIconSize(QSize(32, 32))
|
||||
self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
|
||||
self.bar.setMovable(False)
|
||||
self.bar.setFloatable(False)
|
||||
self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
|
||||
@ -197,13 +220,8 @@ class Preferences(QMainWindow):
|
||||
self.commit)
|
||||
self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
|
||||
_('&Cancel'), self.cancel)
|
||||
self.bar_filler = QLabel('')
|
||||
self.bar_filler.setSizePolicy(QSizePolicy.Expanding,
|
||||
QSizePolicy.Preferred)
|
||||
self.bar_filler.setStyleSheet(
|
||||
'QLabel { font-weight: bold }')
|
||||
self.bar_filler.setAlignment(Qt.AlignHCenter | Qt.AlignCenter)
|
||||
self.bar.addWidget(self.bar_filler)
|
||||
self.bar_title = BarTitle(self.bar)
|
||||
self.bar.addWidget(self.bar_title)
|
||||
self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
|
||||
_('Restore &defaults'), self.restore_defaults)
|
||||
for ac, tt in [('apply', _('Save changes')),
|
||||
@ -247,7 +265,7 @@ class Preferences(QMainWindow):
|
||||
self.restore_action.setToolTip(textwrap.fill(tt))
|
||||
self.restore_action.setWhatsThis(textwrap.fill(tt))
|
||||
self.restore_action.setStatusTip(tt)
|
||||
self.bar_filler.setText(plugin.gui_name)
|
||||
self.bar_title.show_plugin(plugin)
|
||||
self.setWindowIcon(QIcon(plugin.icon))
|
||||
self.bar.setVisible(True)
|
||||
|
||||
|
@ -20,7 +20,7 @@ from calibre.utils.date import isoformat, now as nowf
|
||||
from calibre.utils.logging import default_log as log
|
||||
|
||||
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',
|
||||
'uuid']
|
||||
|
||||
@ -96,6 +96,11 @@ class CSV_XML(CatalogPlugin):
|
||||
# Get the requested output fields as a list
|
||||
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':
|
||||
outfile = codecs.open(path_to_output, 'w', 'utf8')
|
||||
|
||||
@ -104,7 +109,6 @@ class CSV_XML(CatalogPlugin):
|
||||
|
||||
# Output the entry fields
|
||||
for entry in data:
|
||||
print "%s [%s] ondevice: %s" % (entry['title'],entry['id'], repr(db.catalog_plugin_on_device_temp_mapping[entry['id']]))
|
||||
outstr = []
|
||||
for field in fields:
|
||||
item = entry[field]
|
||||
@ -142,10 +146,10 @@ class CSV_XML(CatalogPlugin):
|
||||
root.append(record)
|
||||
|
||||
for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size',
|
||||
'isbn'):
|
||||
'isbn','ondevice'):
|
||||
if field in fields:
|
||||
val = r[field]
|
||||
if val is None:
|
||||
if not val:
|
||||
continue
|
||||
if not isinstance(val, (str, unicode)):
|
||||
val = unicode(val)
|
||||
@ -563,6 +567,13 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
help=_("Include 'Titles' section in catalog.\n"
|
||||
"Default: '%default'\n"
|
||||
"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',
|
||||
default=False,
|
||||
dest='generate_recently_added',
|
||||
@ -888,6 +899,9 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.__totalSteps += 2
|
||||
if self.generateRecentlyRead:
|
||||
self.__totalSteps += 2
|
||||
if self.opts.generate_series:
|
||||
self.__totalSteps += 2
|
||||
|
||||
|
||||
# Accessors
|
||||
if True:
|
||||
@ -1198,6 +1212,8 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.generateHTMLByAuthor()
|
||||
if self.opts.generate_titles:
|
||||
self.generateHTMLByTitle()
|
||||
if self.opts.generate_series:
|
||||
self.generateHTMLBySeries()
|
||||
if self.opts.generate_recently_added:
|
||||
self.generateHTMLByDateAdded()
|
||||
if self.generateRecentlyRead:
|
||||
@ -1211,6 +1227,8 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
self.generateNCXByAuthor("Authors")
|
||||
if self.opts.generate_titles:
|
||||
self.generateNCXByTitle("Titles")
|
||||
if self.opts.generate_series:
|
||||
self.generateNCXBySeries("Series")
|
||||
if self.opts.generate_recently_added:
|
||||
self.generateNCXByDateAdded("Recently Added")
|
||||
if self.generateRecentlyRead:
|
||||
@ -1571,6 +1589,19 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
emTag = Tag(soup, "em")
|
||||
if title['series']:
|
||||
# title<br />series series_index
|
||||
if self.opts.generate_series:
|
||||
brTag = Tag(soup,'br')
|
||||
title_tokens = list(title['title'].partition(':'))
|
||||
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())))
|
||||
@ -1726,7 +1757,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
body.insert(btc, aTag)
|
||||
btc += 1
|
||||
|
||||
'''
|
||||
if not self.__generateForKindle:
|
||||
# We don't need this because the Kindle shows section titles
|
||||
#<h2><a name="byalphatitle" id="byalphatitle"></a>By Title</h2>
|
||||
h2Tag = Tag(soup, "h2")
|
||||
@ -1736,7 +1767,6 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle)))
|
||||
body.insert(btc,h2Tag)
|
||||
btc += 1
|
||||
'''
|
||||
|
||||
# <p class="letter_index">
|
||||
# <p class="book_title">
|
||||
@ -1931,7 +1961,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
current_series = book['series']
|
||||
pSeriesTag = Tag(soup,'p')
|
||||
pSeriesTag['class'] = "series"
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % book['series']))
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
|
||||
divTag.insert(dtc,pSeriesTag)
|
||||
dtc += 1
|
||||
if current_series and not book['series']:
|
||||
@ -1971,7 +2001,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
divTag.insert(dtc, pBookTag)
|
||||
dtc += 1
|
||||
|
||||
'''
|
||||
if not self.__generateForKindle:
|
||||
# Insert the <h2> tag with book_count at the head
|
||||
#<h2><a name="byalphaauthor" id="byalphaauthor"></a>By Author</h2>
|
||||
h2Tag = Tag(soup, "h2")
|
||||
@ -1982,7 +2012,6 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count)))
|
||||
body.insert(btc,h2Tag)
|
||||
btc += 1
|
||||
'''
|
||||
|
||||
# Add the divTag to the body
|
||||
body.insert(btc, divTag)
|
||||
@ -2048,7 +2077,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
current_series = new_entry['series']
|
||||
pSeriesTag = Tag(soup,'p')
|
||||
pSeriesTag['class'] = "series"
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % new_entry['series']))
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % new_entry['series']))
|
||||
divTag.insert(dtc,pSeriesTag)
|
||||
dtc += 1
|
||||
if current_series and not new_entry['series']:
|
||||
@ -2162,8 +2191,8 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
aTag['name'] = anchor_name.replace(" ","")
|
||||
body.insert(btc, aTag)
|
||||
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>
|
||||
h2Tag = Tag(soup, "h2")
|
||||
aTag = Tag(soup, "a")
|
||||
@ -2173,7 +2202,6 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
h2Tag.insert(1,NavigableString('%s' % friendly_name))
|
||||
body.insert(btc,h2Tag)
|
||||
btc += 1
|
||||
'''
|
||||
|
||||
# <p class="letter_index">
|
||||
# <p class="author_index">
|
||||
@ -2439,6 +2467,158 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
outfile.close()
|
||||
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)
|
||||
|
||||
for series_item in self.booksBySeries:
|
||||
print ' %s %s %s' % (series_item['series'],series_item['series_index'],series_item['title'])
|
||||
|
||||
friendly_name = "By 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 · %s' % (book['series_index'],escape(book['title']), ' & '.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>
|
||||
h2Tag = Tag(soup, "h2")
|
||||
aTag = Tag(soup, "a")
|
||||
anchor_name = friendly_name.lower()
|
||||
aTag['name'] = anchor_name.replace(" ","")
|
||||
h2Tag.insert(0,aTag)
|
||||
h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, series_count)))
|
||||
body.insert(btc,h2Tag)
|
||||
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):
|
||||
# Generate individual HTML files for each tag, e.g. Fiction, Nonfiction ...
|
||||
# Note that special tags - ~+*[] - have already been filtered from books[]
|
||||
@ -2884,6 +3064,98 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
|
||||
self.ncxSoup = ncx_soup
|
||||
|
||||
def generateNCXBySeries(self, tocTitle):
|
||||
self.updateProgressFullStep("NCX 'Series'")
|
||||
|
||||
def add_to_series_by_letter(current_series_list):
|
||||
current_series_list = " • ".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):
|
||||
self.updateProgressFullStep("NCX 'Titles'")
|
||||
|
||||
@ -3754,7 +4026,7 @@ class EPUB_MOBI(CatalogPlugin):
|
||||
current_series = book['series']
|
||||
pSeriesTag = Tag(soup,'p')
|
||||
pSeriesTag['class'] = "series"
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % book['series']))
|
||||
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
|
||||
divTag.insert(dtc,pSeriesTag)
|
||||
dtc += 1
|
||||
|
||||
|
@ -676,7 +676,7 @@ def command_catalog(args, dbpath):
|
||||
# Parallel initialization in calibre.gui2.tools:generate_catalog()
|
||||
opts.connected_device = {
|
||||
'is_device_connected': False,
|
||||
'kind': device_manager.connected_device_kind,
|
||||
'kind': None,
|
||||
'name': None,
|
||||
'save_template': None,
|
||||
'serial': None,
|
||||
|
Loading…
x
Reference in New Issue
Block a user