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
|
# 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))))
|
||||||
|
@ -34,11 +34,10 @@ class GenerateCatalogAction(InterfaceAction):
|
|||||||
db = self.gui.library_view.model().db
|
db = self.gui.library_view.model().db
|
||||||
dbspec = {}
|
dbspec = {}
|
||||||
for id in ids:
|
for id in ids:
|
||||||
dbspec[id] = {'ondevice':db.ondevice(id, index_is_id=True)}
|
dbspec[id] = {'ondevice': db.ondevice(id, index_is_id=True)}
|
||||||
db.catalog_plugin_on_device_temp_mapping = dbspec
|
|
||||||
|
|
||||||
# Calling gui2.tools:generate_catalog()
|
# 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:
|
if ret is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -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(',')
|
||||||
|
@ -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/>
|
||||||
|
@ -27,13 +27,9 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne
|
|||||||
notification=DummyReporter(), log=None):
|
notification=DummyReporter(), log=None):
|
||||||
if log is None:
|
if log is None:
|
||||||
log = Log()
|
log = Log()
|
||||||
if dbspec is None:
|
from calibre.library import db
|
||||||
from calibre.utils.config import prefs
|
db = db()
|
||||||
from calibre.library.database2 import LibraryDatabase2
|
db.catalog_plugin_on_device_temp_mapping = dbspec
|
||||||
dbpath = prefs['library_path']
|
|
||||||
db = LibraryDatabase2(dbpath)
|
|
||||||
else: # To be implemented in the future
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Create a minimal OptionParser that we can append to
|
# Create a minimal OptionParser that we can append to
|
||||||
parser = OptionParser()
|
parser = OptionParser()
|
||||||
|
@ -10,8 +10,8 @@ from functools import partial
|
|||||||
|
|
||||||
from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \
|
from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \
|
||||||
QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \
|
QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \
|
||||||
QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction, \
|
QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \
|
||||||
QPushButton, QHBoxLayout
|
QDialogButtonBox, QHBoxLayout
|
||||||
|
|
||||||
from calibre.constants import __appname__, __version__, islinux, isosx
|
from calibre.constants import __appname__, __version__, islinux, isosx
|
||||||
from calibre.gui2 import gprefs, min_available_height, available_width, \
|
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.customize.ui import preferences_plugins
|
||||||
from calibre.utils.ordered_dict import OrderedDict
|
from calibre.utils.ordered_dict import OrderedDict
|
||||||
|
|
||||||
|
ICON_SIZE = 32
|
||||||
|
|
||||||
class StatusBar(QStatusBar): # {{{
|
class StatusBar(QStatusBar): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
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): # {{{
|
class Category(QWidget): # {{{
|
||||||
|
|
||||||
plugin_activated = pyqtSignal(object)
|
plugin_activated = pyqtSignal(object)
|
||||||
@ -88,7 +116,6 @@ class Category(QWidget): # {{{
|
|||||||
class Browser(QScrollArea): # {{{
|
class Browser(QScrollArea): # {{{
|
||||||
|
|
||||||
show_plugin = pyqtSignal(object)
|
show_plugin = pyqtSignal(object)
|
||||||
close_signal = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QScrollArea.__init__(self, parent)
|
QScrollArea.__init__(self, parent)
|
||||||
@ -119,14 +146,6 @@ class Browser(QScrollArea): # {{{
|
|||||||
self.container = QWidget(self)
|
self.container = QWidget(self)
|
||||||
self.container.setLayout(self._layout)
|
self.container.setLayout(self._layout)
|
||||||
self.setWidget(self.container)
|
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():
|
for name, plugins in self.category_map.items():
|
||||||
w = Category(name, plugins, self)
|
w = Category(name, plugins, self)
|
||||||
@ -134,8 +153,6 @@ class Browser(QScrollArea): # {{{
|
|||||||
self._layout.addWidget(w)
|
self._layout.addWidget(w)
|
||||||
w.plugin_activated.connect(self.show_plugin.emit)
|
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.setStatusBar(self.status_bar)
|
||||||
|
|
||||||
self.stack = QStackedWidget(self)
|
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 = Browser(self)
|
||||||
self.browser.close_signal.connect(self.close, type=Qt.QueuedConnection)
|
|
||||||
self.browser.show_plugin.connect(self.show_plugin)
|
self.browser.show_plugin.connect(self.show_plugin)
|
||||||
self.stack.addWidget(self.browser)
|
self.stack.addWidget(self.browser)
|
||||||
self.scroll_area = QScrollArea(self)
|
self.scroll_area = QScrollArea(self)
|
||||||
@ -189,7 +212,7 @@ class Preferences(QMainWindow):
|
|||||||
self.bar = QToolBar(self)
|
self.bar = QToolBar(self)
|
||||||
self.addToolBar(self.bar)
|
self.addToolBar(self.bar)
|
||||||
self.bar.setVisible(False)
|
self.bar.setVisible(False)
|
||||||
self.bar.setIconSize(QSize(32, 32))
|
self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
|
||||||
self.bar.setMovable(False)
|
self.bar.setMovable(False)
|
||||||
self.bar.setFloatable(False)
|
self.bar.setFloatable(False)
|
||||||
self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
|
self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
|
||||||
@ -197,13 +220,8 @@ class Preferences(QMainWindow):
|
|||||||
self.commit)
|
self.commit)
|
||||||
self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
|
self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')),
|
||||||
_('&Cancel'), self.cancel)
|
_('&Cancel'), self.cancel)
|
||||||
self.bar_filler = QLabel('')
|
self.bar_title = BarTitle(self.bar)
|
||||||
self.bar_filler.setSizePolicy(QSizePolicy.Expanding,
|
self.bar.addWidget(self.bar_title)
|
||||||
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.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
|
self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')),
|
||||||
_('Restore &defaults'), self.restore_defaults)
|
_('Restore &defaults'), self.restore_defaults)
|
||||||
for ac, tt in [('apply', _('Save changes')),
|
for ac, tt in [('apply', _('Save changes')),
|
||||||
@ -247,7 +265,7 @@ class Preferences(QMainWindow):
|
|||||||
self.restore_action.setToolTip(textwrap.fill(tt))
|
self.restore_action.setToolTip(textwrap.fill(tt))
|
||||||
self.restore_action.setWhatsThis(textwrap.fill(tt))
|
self.restore_action.setWhatsThis(textwrap.fill(tt))
|
||||||
self.restore_action.setStatusTip(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.setWindowIcon(QIcon(plugin.icon))
|
||||||
self.bar.setVisible(True)
|
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
|
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']
|
||||||
|
|
||||||
@ -96,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')
|
||||||
|
|
||||||
@ -104,7 +109,6 @@ class CSV_XML(CatalogPlugin):
|
|||||||
|
|
||||||
# Output the entry fields
|
# Output the entry fields
|
||||||
for entry in data:
|
for entry in data:
|
||||||
print "%s [%s] ondevice: %s" % (entry['title'],entry['id'], repr(db.catalog_plugin_on_device_temp_mapping[entry['id']]))
|
|
||||||
outstr = []
|
outstr = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
item = entry[field]
|
item = entry[field]
|
||||||
@ -142,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)
|
||||||
@ -563,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',
|
||||||
@ -888,6 +899,9 @@ 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:
|
if True:
|
||||||
@ -1198,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:
|
||||||
@ -1211,6 +1227,8 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
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:
|
||||||
@ -1571,13 +1589,26 @@ 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
|
||||||
brTag = Tag(soup,'br')
|
if self.opts.generate_series:
|
||||||
title_tokens = list(title['title'].partition(':'))
|
brTag = Tag(soup,'br')
|
||||||
emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
|
title_tokens = list(title['title'].partition(':'))
|
||||||
emTag.insert(1, brTag)
|
emTag.insert(0, escape(NavigableString(title_tokens[2].strip())))
|
||||||
smallTag = Tag(soup,'small')
|
emTag.insert(1, brTag)
|
||||||
smallTag.insert(0, escape(NavigableString(title_tokens[0])))
|
smallTag = Tag(soup,'small')
|
||||||
emTag.insert(2, smallTag)
|
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)
|
||||||
|
smallTag = Tag(soup,'small')
|
||||||
|
smallTag.insert(0, escape(NavigableString(title_tokens[0])))
|
||||||
|
emTag.insert(2, smallTag)
|
||||||
else:
|
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'})
|
||||||
@ -1726,17 +1757,16 @@ 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")
|
h2Tag = Tag(soup, "h2")
|
||||||
aTag = Tag(soup, "a")
|
aTag = Tag(soup, "a")
|
||||||
aTag['name'] = "bytitle"
|
aTag['name'] = "bytitle"
|
||||||
h2Tag.insert(0,aTag)
|
h2Tag.insert(0,aTag)
|
||||||
h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle)))
|
h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle)))
|
||||||
body.insert(btc,h2Tag)
|
body.insert(btc,h2Tag)
|
||||||
btc += 1
|
btc += 1
|
||||||
'''
|
|
||||||
|
|
||||||
# <p class="letter_index">
|
# <p class="letter_index">
|
||||||
# <p class="book_title">
|
# <p class="book_title">
|
||||||
@ -1931,7 +1961,7 @@ 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 + '%s Series' % book['series']))
|
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%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']:
|
||||||
@ -1971,18 +2001,17 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
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")
|
h2Tag = Tag(soup, "h2")
|
||||||
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)
|
h2Tag.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)
|
body.insert(btc,h2Tag)
|
||||||
btc += 1
|
btc += 1
|
||||||
'''
|
|
||||||
|
|
||||||
# Add the divTag to the body
|
# Add the divTag to the body
|
||||||
body.insert(btc, divTag)
|
body.insert(btc, divTag)
|
||||||
@ -2048,7 +2077,7 @@ 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 + '%s Series' % new_entry['series']))
|
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%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']:
|
||||||
@ -2162,18 +2191,17 @@ 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")
|
h2Tag = Tag(soup, "h2")
|
||||||
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)
|
h2Tag.insert(0,aTag)
|
||||||
h2Tag.insert(1,NavigableString('%s' % friendly_name))
|
h2Tag.insert(1,NavigableString('%s' % friendly_name))
|
||||||
body.insert(btc,h2Tag)
|
body.insert(btc,h2Tag)
|
||||||
btc += 1
|
btc += 1
|
||||||
'''
|
|
||||||
|
|
||||||
# <p class="letter_index">
|
# <p class="letter_index">
|
||||||
# <p class="author_index">
|
# <p class="author_index">
|
||||||
@ -2439,6 +2467,158 @@ 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)
|
||||||
|
|
||||||
|
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):
|
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[]
|
||||||
@ -2884,6 +3064,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 = " • ".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'")
|
||||||
|
|
||||||
@ -3754,7 +4026,7 @@ 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 + '%s Series' % book['series']))
|
pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series']))
|
||||||
divTag.insert(dtc,pSeriesTag)
|
divTag.insert(dtc,pSeriesTag)
|
||||||
dtc += 1
|
dtc += 1
|
||||||
|
|
||||||
|
@ -676,7 +676,7 @@ def command_catalog(args, dbpath):
|
|||||||
# Parallel initialization in calibre.gui2.tools:generate_catalog()
|
# Parallel initialization in calibre.gui2.tools:generate_catalog()
|
||||||
opts.connected_device = {
|
opts.connected_device = {
|
||||||
'is_device_connected': False,
|
'is_device_connected': False,
|
||||||
'kind': device_manager.connected_device_kind,
|
'kind': None,
|
||||||
'name': None,
|
'name': None,
|
||||||
'save_template': None,
|
'save_template': None,
|
||||||
'serial': None,
|
'serial': None,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user