GwR catalog UI revisions

This commit is contained in:
GRiker 2010-12-23 12:49:53 -07:00
parent 3c254aca3f
commit 230d92dcb5
7 changed files with 1114 additions and 275 deletions

View File

@ -108,6 +108,13 @@ p.date_read {
text-indent:-6em; text-indent:-6em;
} }
hr.annotations_divider {
width:50%;
margin-left:1em;
margin-top:0em;
margin-bottom:0em;
}
hr.description_divider { hr.description_divider {
width:90%; width:90%;
margin-left:5%; margin-left:5%;
@ -117,18 +124,20 @@ hr.description_divider {
border-left: solid white 0px; border-left: solid white 0px;
} }
hr.annotations_divider { hr.merged_comments_divider {
width:50%; width:80%;
margin-left:1em; margin-left:10%;
margin-top:0em; border-top: solid white 0px;
margin-bottom:0em; border-right: solid white 0px;
border-bottom: dotted grey 2px;
border-left: solid white 0px;
} }
td.publisher, td.date { td.publisher, td.date {
font-weight:bold; font-weight:bold;
text-align:center; text-align:center;
} }
td.rating { td.rating, td.notes {
text-align: center; text-align: center;
} }
td.thumbnail img { td.thumbnail img {

View File

@ -57,7 +57,7 @@ class GenerateCatalogAction(InterfaceAction):
if job.result: if job.result:
# Search terms nulled catalog results # Search terms nulled catalog results
return error_dialog(self.gui, _('No books found'), return error_dialog(self.gui, _('No books found'),
_("No books to catalog\nCheck exclude tags"), _("No books to catalog\nCheck exclusion criteria"),
show=True) show=True)
if job.failed: if job.failed:
return self.gui.job_exception(job) return self.gui.job_exception(job)

View File

@ -17,18 +17,55 @@ class PluginWidget(QWidget,Ui_Form):
TITLE = _('E-book options') TITLE = _('E-book options')
HELP = _('Options specific to')+' EPUB/MOBI '+_('output') HELP = _('Options specific to')+' EPUB/MOBI '+_('output')
OPTION_FIELDS = [('exclude_genre','\[.+\]'),
('exclude_tags','~,'+_('Catalog')),
('generate_titles', True),
('generate_series', True),
('generate_recently_added', True),
('note_tag','*'),
('numbers_as_text', False),
('read_pattern','+'),
('read_source_field_cb','Tag'),
('wishlist_tag','Wishlist'),
]
CheckBoxControls = [
'generate_titles',
'generate_series',
'generate_genres',
'generate_recently_added',
'generate_descriptions',
'include_hr'
]
ComboBoxControls = [
'read_source_field',
'exclude_source_field',
'header_note_source_field',
'merge_source_field'
]
LineEditControls = [
'exclude_genre',
'exclude_pattern',
'exclude_tags',
'read_pattern',
'wishlist_tag'
]
RadioButtonControls = [
'merge_before',
'merge_after'
]
SpinBoxControls = [
'thumb_width'
]
OPTION_FIELDS = zip(CheckBoxControls,
[True for i in CheckBoxControls],
['check_box' for i in CheckBoxControls])
OPTION_FIELDS += zip(ComboBoxControls,
[None for i in ComboBoxControls],
['combo_box' for i in ComboBoxControls])
OPTION_FIELDS += zip(RadioButtonControls,
[None for i in RadioButtonControls],
['radio_button' for i in RadioButtonControls])
# LineEditControls
OPTION_FIELDS += zip(['exclude_genre'],['\[.+\]'],['line_edit'])
OPTION_FIELDS += zip(['exclude_pattern'],[None],['line_edit'])
OPTION_FIELDS += zip(['exclude_tags'],['~,'+_('Catalog')],['line_edit'])
OPTION_FIELDS += zip(['read_pattern'],['+'],['line_edit'])
OPTION_FIELDS += zip(['wishlist_tag'],['Wishlist'],['line_edit'])
# SpinBoxControls
OPTION_FIELDS += zip(['thumb_width'],[1.00],['spin_box'])
# Output synced to the connected device? # Output synced to the connected device?
sync_enabled = True sync_enabled = True
@ -42,105 +79,203 @@ class PluginWidget(QWidget,Ui_Form):
def initialize(self, name, db): def initialize(self, name, db):
self.name = name self.name = name
self.db = db
# Populate the 'Read book' source fields self.populateComboBoxes()
all_custom_fields = db.custom_field_keys()
custom_fields = {}
custom_fields['Tag'] = {'field':'tag', 'datatype':u'text'}
for custom_field in all_custom_fields:
field_md = db.metadata_for_field(custom_field)
if field_md['datatype'] in ['bool','composite','datetime','text']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
# Add the sorted eligible fields to the combo box
for cf in sorted(custom_fields):
self.read_source_field_cb.addItem(cf)
self.read_source_fields = custom_fields
self.read_source_field_cb.currentIndexChanged.connect(self.read_source_field_changed)
# 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]) c_name, c_def, c_type = opt
if opt[0] in [ opt_value = gprefs.get(self.name + '_' + c_name, c_def)
'generate_recently_added', if c_type in ['check_box']:
'generate_series', getattr(self, c_name).setChecked(eval(str(opt_value)))
'generate_titles', elif c_type in ['combo_box'] and opt_value is not None:
'numbers_as_text', # *** Test this code with combo boxes ***
]: #index = self.read_source_field.findText(opt_value)
getattr(self, opt[0]).setChecked(opt_value) index = getattr(self,c_name).findText(opt_value)
if index == -1 and c_name == 'read_source_field':
index = self.read_source_field.findText('Tag')
#self.read_source_field.setCurrentIndex(index)
getattr(self,c_name).setCurrentIndex(index)
elif c_type in ['line_edit']:
getattr(self, c_name).setText(opt_value)
elif c_type in ['radio_button'] and opt_value is not None:
getattr(self, c_name).setChecked(opt_value)
elif c_type in ['spin_box']:
getattr(self, c_name).setValue(float(opt_value))
# Combo box # Init self.read_source_field_name
elif opt[0] in ['read_source_field_cb']: cs = unicode(self.read_source_field.currentText())
# Look for last-stored combo box value
index = self.read_source_field_cb.findText(opt_value)
if index == -1:
index = self.read_source_field_cb.findText('Tag')
self.read_source_field_cb.setCurrentIndex(index)
# Text fields
else:
getattr(self, opt[0]).setText(opt_value)
# Init self.read_source_field
cs = unicode(self.read_source_field_cb.currentText())
read_source_spec = self.read_source_fields[cs] read_source_spec = self.read_source_fields[cs]
self.read_source_field = read_source_spec['field'] self.read_source_field_name = read_source_spec['field']
# Init self.exclude_source_field_name
self.exclude_source_field_name = ''
cs = unicode(self.exclude_source_field.currentText())
if cs > '':
exclude_source_spec = self.exclude_source_fields[cs]
self.exclude_source_field_name = exclude_source_spec['field']
# Init self.merge_source_field_name
self.merge_source_field_name = ''
cs = unicode(self.merge_source_field.currentText())
if cs > '':
merge_source_spec = self.merge_source_fields[cs]
self.merge_source_field_name = merge_source_spec['field']
# Init self.header_note_source_field_name
self.header_note_source_field_name = ''
cs = unicode(self.header_note_source_field.currentText())
if cs > '':
header_note_source_spec = self.header_note_source_fields[cs]
self.header_note_source_field_name = header_note_source_spec['field']
# Hook changes to thumb_width
self.thumb_width.valueChanged.connect(self.thumb_width_changed)
def options(self): def options(self):
# Save/return the current options # Save/return the current options
# exclude_genre stores literally # exclude_genre stores literally
# generate_titles, generate_recently_added, numbers_as_text stores as True/False # generate_titles, generate_recently_added, numbers_as_text stores as True/False
# others store as lists # others store as lists
opts_dict = {} opts_dict = {}
# Save values to gprefs
for opt in self.OPTION_FIELDS: for opt in self.OPTION_FIELDS:
# Save values to gprefs c_name, c_def, c_type = opt
if opt[0] in [ if c_type in ['check_box', 'radio_button']:
'generate_recently_added', opt_value = getattr(self, c_name).isChecked()
'generate_series', elif c_type in ['combo_box']:
'generate_titles', opt_value = unicode(getattr(self,c_name).currentText())
'numbers_as_text', elif c_type in ['line_edit']:
]: opt_value = unicode(getattr(self, c_name).text())
opt_value = getattr(self,opt[0]).isChecked() elif c_type in ['spin_box']:
opt_value = unicode(getattr(self, c_name).cleanText())
gprefs.set(self.name + '_' + c_name, opt_value)
# Combo box uses .currentText() # Construct opts object
elif opt[0] in ['read_source_field_cb']: if c_name == 'exclude_tags':
opt_value = unicode(getattr(self, opt[0]).currentText()) # store as list
opts_dict[c_name] = opt_value.split(',')
# text fields use .text()
else: else:
opt_value = unicode(getattr(self, opt[0]).text()) opts_dict[c_name] = opt_value
gprefs.set(self.name + '_' + opt[0], opt_value)
# Construct opts # Generate markers for hybrids
if opt[0] in [ opts_dict['read_book_marker'] = "%s:%s" % (self.read_source_field_name,
'exclude_genre', self.read_pattern.text())
'generate_recently_added', opts_dict['exclude_book_marker'] = "%s:%s" % (self.exclude_source_field_name,
'generate_series', self.exclude_pattern.text())
'generate_titles',
'numbers_as_text',
]:
opts_dict[opt[0]] = opt_value
else:
opts_dict[opt[0]] = opt_value.split(',')
# Generate read_book_marker # Generate specs for merge_comments, header_note_source_field
opts_dict['read_book_marker'] = "%s:%s" % (self.read_source_field, self.read_pattern.text()) checked = ''
if self.merge_before.isChecked():
checked = 'before'
elif self.merge_after.isChecked():
checked = 'after'
include_hr = self.include_hr.isChecked()
opts_dict['merge_comments'] = "%s:%s:%s" % \
(self.merge_source_field_name, checked, include_hr)
opts_dict['header_note_source_field'] = self.header_note_source_field_name
# Append the output profile # Append the output profile
opts_dict['output_profile'] = [load_defaults('page_setup')['output_profile']] opts_dict['output_profile'] = [load_defaults('page_setup')['output_profile']]
if False:
print "opts_dict"
for opt in sorted(opts_dict.keys()):
print " %s: %s" % (opt, repr(opts_dict[opt]))
return opts_dict return opts_dict
def populateComboBoxes(self):
# Custom column types declared in
# gui2.preferences.create_custom_column:CreateCustomColumn()
# As of 0.7.34:
# bool Yes/No
# comments Long text, like comments, not shown in tag browser
# composite Column built from other columns
# datetime Date
# enumeration Text, but with a fixed set of permitted values
# float Floating point numbers
# int Integers
# rating Ratings, shown with stars
# series Text column for keeping series-like information
# text Column shown in the tag browser
# *text Comma-separated text, like tags, shown in tag browser
all_custom_fields = self.db.custom_field_keys()
# Populate the 'Read book' hybrid
custom_fields = {}
custom_fields['Tag'] = {'field':'tag', 'datatype':u'text'}
for custom_field in all_custom_fields:
field_md = self.db.metadata_for_field(custom_field)
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
# Add the sorted eligible fields to the combo box
for cf in sorted(custom_fields):
self.read_source_field.addItem(cf)
self.read_source_fields = custom_fields
self.read_source_field.currentIndexChanged.connect(self.read_source_field_changed)
# Populate the 'Excluded books' hybrid
custom_fields = {}
for custom_field in all_custom_fields:
field_md = self.db.metadata_for_field(custom_field)
if field_md['datatype'] in ['bool','composite','datetime','enumeration','text']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
# Blank field first
self.exclude_source_field.addItem('')
# Add the sorted eligible fields to the combo box
for cf in sorted(custom_fields):
self.exclude_source_field.addItem(cf)
self.exclude_source_fields = custom_fields
self.exclude_source_field.currentIndexChanged.connect(self.exclude_source_field_changed)
# Populate the 'Header note' combo box
custom_fields = {}
for custom_field in all_custom_fields:
field_md = self.db.metadata_for_field(custom_field)
if field_md['datatype'] in ['composite','datetime','enumeration','text']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
# Blank field first
self.header_note_source_field.addItem('')
# Add the sorted eligible fields to the combo box
for cf in sorted(custom_fields):
self.header_note_source_field.addItem(cf)
self.header_note_source_fields = custom_fields
self.header_note_source_field.currentIndexChanged.connect(self.header_note_source_field_changed)
# Populate the 'Merge with Comments' combo box
custom_fields = {}
for custom_field in all_custom_fields:
field_md = self.db.metadata_for_field(custom_field)
if field_md['datatype'] in ['text','comments']:
custom_fields[field_md['name']] = {'field':custom_field,
'datatype':field_md['datatype']}
# Blank field first
self.merge_source_field.addItem('')
# Add the sorted eligible fields to the combo box
for cf in sorted(custom_fields):
self.merge_source_field.addItem(cf)
self.merge_source_fields = custom_fields
self.merge_source_field.currentIndexChanged.connect(self.merge_source_field_changed)
self.merge_before.setEnabled(False)
self.merge_after.setEnabled(False)
self.include_hr.setEnabled(False)
def read_source_field_changed(self,new_index): def read_source_field_changed(self,new_index):
''' '''
Process changes in the read_source_field combo box Process changes in the read_source_field combo box
Currently using QLineEdit for all field types Currently using QLineEdit for all field types
Possible to modify to switch QWidget type Possible to modify to switch QWidget type
''' '''
new_source = str(self.read_source_field_cb.currentText()) new_source = str(self.read_source_field.currentText())
read_source_spec = self.read_source_fields[str(new_source)] read_source_spec = self.read_source_fields[str(new_source)]
self.read_source_field = read_source_spec['field'] self.read_source_field_name = read_source_spec['field']
# Change pattern input widget to match the source field datatype # Change pattern input widget to match the source field datatype
if read_source_spec['datatype'] in ['bool','composite','datetime','text']: if read_source_spec['datatype'] in ['bool','composite','datetime','text']:
@ -152,3 +287,62 @@ class PluginWidget(QWidget,Ui_Form):
self.read_pattern = dw self.read_pattern = dw
self.read_spec_hl.addWidget(dw) self.read_spec_hl.addWidget(dw)
def exclude_source_field_changed(self,new_index):
'''
Process changes in the exclude_source_field combo box
Currently using QLineEdit for all field types
Possible to modify to switch QWidget type
'''
new_source = str(self.exclude_source_field.currentText())
self.exclude_source_field_name = new_source
if new_source > '':
exclude_source_spec = self.exclude_source_fields[str(new_source)]
self.exclude_source_field_name = exclude_source_spec['field']
# Change pattern input widget to match the source field datatype
if exclude_source_spec['datatype'] in ['bool','composite','datetime','text']:
if not isinstance(self.exclude_pattern, QLineEdit):
self.exclude_spec_hl.removeWidget(self.exclude_pattern)
dw = QLineEdit(self)
dw.setObjectName('exclude_pattern')
dw.setToolTip('Exclusion pattern')
self.exclude_pattern = dw
self.exclude_spec_hl.addWidget(dw)
else:
self.exclude_pattern.setText('')
def header_note_source_field_changed(self,new_index):
'''
Process changes in the header_note_source_field combo box
'''
new_source = str(self.header_note_source_field.currentText())
self.header_note_source_field_name = new_source
if new_source > '':
header_note_source_spec = self.header_note_source_fields[str(new_source)]
self.header_note_source_field_name = header_note_source_spec['field']
def merge_source_field_changed(self,new_index):
'''
Process changes in the header_note_source_field combo box
'''
new_source = str(self.merge_source_field.currentText())
self.merge_source_field_name = new_source
if new_source > '':
merge_source_spec = self.merge_source_fields[str(new_source)]
self.merge_source_field_name = merge_source_spec['field']
if not self.merge_before.isChecked() and not self.merge_after.isChecked():
self.merge_after.setChecked(True)
self.merge_before.setEnabled(True)
self.merge_after.setEnabled(True)
self.include_hr.setEnabled(True)
else:
self.merge_before.setEnabled(False)
self.merge_after.setEnabled(False)
self.include_hr.setEnabled(False)
def thumb_width_changed(self,new_value):
'''
Process changes in the thumb_width spin box
'''
pass

View File

@ -6,163 +6,653 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>627</width> <width>650</width>
<height>549</height> <height>582</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item row="0" column="0"> <item>
<widget class="QLabel" name="label_2"> <widget class="QGroupBox" name="includedSections">
<property name="text"> <property name="sizePolicy">
<string>'Don't include this book' tag:</string> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
</widget> <property name="minimumSize">
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="exclude_tags">
<property name="toolTip">
<string extracomment="Default: ~,Catalog"/>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Additional note tag prefix:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="note_tag">
<property name="toolTip">
<string extracomment="Default: *"/>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLineEdit" name="exclude_genre">
<property name="toolTip">
<string extracomment="Default: \[[\w]*\]"/>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Regex pattern describing tags to exclude as genres:</string>
</property>
<property name="textFormat">
<enum>Qt::LogText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Regex tips:
- The default regex - \[.+\] - excludes genre tags of the form [tag], e.g., [Amazon Freebie]
- A regex pattern of a single dot excludes all genre tags, generating no Genre Section</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>0</width>
<height>40</height> <height>0</height>
</size> </size>
</property> </property>
</spacer> <property name="toolTip">
</item> <string>Sections to include in generated catalog. A minimal catalog includes 'Books by Author'.</string>
<item row="10" column="0">
<widget class="QCheckBox" name="generate_titles">
<property name="text">
<string>Include 'Titles' Section</string>
</property> </property>
<property name="title">
<string>Included sections (Books by Author included by default)</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="generate_titles">
<property name="text">
<string>Books by Title</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="generate_series">
<property name="text">
<string>Books by Series</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QCheckBox" name="generate_recently_added">
<property name="text">
<string>Recently Added</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="generate_genres">
<property name="text">
<string>Books by Genre</string>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QCheckBox" name="generate_descriptions">
<property name="text">
<string>Descriptions</string>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="12" column="0"> <item>
<widget class="QCheckBox" name="generate_recently_added"> <widget class="QGroupBox" name="excludedGenres">
<property name="text"> <property name="sizePolicy">
<string>Include 'Recently Added' Section</string> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Default pattern &lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'Courier New,courier';&quot;&gt;\[.+\]&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;excludes tags of the form [&lt;span style=&quot; font-style:italic;&quot;&gt;tag&lt;/span&gt;]&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="title">
<string>Excluded genres</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Tags to exclude</string>
</property>
<property name="textFormat">
<enum>Qt::AutoText</enum>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="exclude_genre">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string extracomment="Default: \[[\w]*\]"/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="13" column="0"> <item>
<widget class="QCheckBox" name="numbers_as_text"> <widget class="QGroupBox" name="excludedBooks">
<property name="text"> <property name="sizePolicy">
<string>Sort numbers as text</string> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Exclude matching books from generated catalog</string>
</property>
<property name="title">
<string>Excluded books</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Tags to exclude</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="exclude_tags">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Lucida Grande'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Comma-separated list of tags to exclude.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:12pt;&quot;&gt;Default:&lt;/span&gt;&lt;span style=&quot; font-family:'Courier New,courier'; font-size:12pt;&quot;&gt; ~,Catalog&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="exclude_spec_hl">
<item>
<widget class="QLabel" name="label_7">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Column/value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="exclude_source_field">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Column containing exclusion criteria</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="minimumContentsLength">
<number>18</number>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="exclude_pattern">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Exclusion pattern</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item>
<widget class="QCheckBox" name="generate_series"> <widget class="QGroupBox" name="readBooks">
<property name="text"> <property name="sizePolicy">
<string>Include 'Series' Section</string> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Matching books will be displayed with ✓</string>
</property>
<property name="title">
<string>Read books</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="read_spec_hl">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
<widget class="QLabel" name="label_3">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Column/value</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="read_source_field">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Column containing 'read' status</string>
</property>
<property name="statusTip">
<string/>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
</property>
<property name="minimumContentsLength">
<number>18</number>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="read_pattern">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>'read book' pattern</string>
</property>
<property name="statusTip">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item>
<widget class="QLineEdit" name="wishlist_tag"/> <widget class="QGroupBox" name="otherOptions">
</item> <property name="sizePolicy">
<item row="3" column="0"> <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<widget class="QLabel" name="label_5"> <horstretch>0</horstretch>
<property name="text"> <verstretch>0</verstretch>
<string>Wishlist tag:</string> </sizepolicy>
</property> </property>
</widget> <property name="minimumSize">
</item> <size>
<item row="2" column="1"> <width>0</width>
<layout class="QHBoxLayout" name="read_spec_hl"> <height>0</height>
<property name="sizeConstraint"> </size>
<enum>QLayout::SetMinimumSize</enum>
</property> </property>
<item> <property name="title">
<widget class="QComboBox" name="read_source_field_cb"> <string>Other options</string>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Source column for read book</string>
</property>
<property name="statusTip">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="read_pattern">
<property name="toolTip">
<string>Pattern for read book</string>
</property>
<property name="statusTip">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Books marked as read:</string>
</property> </property>
<layout class="QFormLayout" name="formLayout_4">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::FieldsStayAtSizeHint</enum>
</property>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_5">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Wishlist tag</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="wishlist_tag">
<property name="toolTip">
<string>Wishlist items will be displayed with ✕</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_4">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Thumbnail width</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="thumb_width">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Size hint for cover thumbnails included in Descriptions</string>
</property>
<property name="suffix">
<string>&quot;</string>
</property>
<property name="decimals">
<number>2</number>
</property>
<property name="minimum">
<double>1.000000000000000</double>
</property>
<property name="maximum">
<double>2.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Header note</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="header_note_source_field">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Column containing header note</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_9">
<property name="minimumSize">
<size>
<width>175</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Merge with Comments</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="merge_source_field">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Column containing additional content to merge</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="merge_before">
<property name="toolTip">
<string>Merge before Comments</string>
</property>
<property name="text">
<string>Before</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="merge_after">
<property name="toolTip">
<string>Merge after Comments</string>
</property>
<property name="text">
<string>After</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="include_hr">
<property name="toolTip">
<string>Separate with horizontal rule</string>
</property>
<property name="text">
<string>&lt;hr /&gt;</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>579</width> <width>650</width>
<height>411</height> <height>575</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>611</width> <width>674</width>
<height>514</height> <height>660</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -33,6 +33,18 @@
</item> </item>
<item row="1" column="0" colspan="2"> <item row="1" column="0" colspan="2">
<widget class="QTabWidget" name="tabs"> <widget class="QTabWidget" name="tabs">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>650</width>
<height>575</height>
</size>
</property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>0</number>
</property> </property>

View File

@ -550,6 +550,13 @@ class EPUB_MOBI(CatalogPlugin):
"of the conversion process a bug is occurring.\n" "of the conversion process a bug is occurring.\n"
"Default: '%default'None\n" "Default: '%default'None\n"
"Applies to: ePub, MOBI output formats")), "Applies to: ePub, MOBI output formats")),
Option('--exclude-book-marker',
default=':',
dest='exclude_book_marker',
action = None,
help=_("field:pattern specifying custom field/contents indicating book should be excluded.\n"
"Default: '%default'\n"
"Applies to ePub, MOBI output formats")),
Option('--exclude-genre', Option('--exclude-genre',
default='\[.+\]', default='\[.+\]',
dest='exclude_genre', dest='exclude_genre',
@ -585,6 +592,23 @@ class EPUB_MOBI(CatalogPlugin):
help=_("Include 'Recently Added' section in catalog.\n" help=_("Include 'Recently Added' section in catalog.\n"
"Default: '%default'\n" "Default: '%default'\n"
"Applies to: ePub, MOBI output formats")), "Applies to: ePub, MOBI output formats")),
Option('--header-note-source-field',
default='',
dest='header_note_source_field',
action = None,
help=_("Custom field containing note text to insert in Description header.\n"
"Default: '%default'\n"
"Applies to: ePub, MOBI output formats")),
Option('--merge-comments',
default='::',
dest='merge_comments',
action = None,
help=_("<custom field>:[before|after]:[True|False] specifying:\n"
" <custom field> Custom field containing notes to merge with Comments\n"
" [before|after] Placement of notes with respect to Comments\n"
" [True|False] - A horizontal rule is inserted between notes and Comments\n"
"Default: '%default'\n"
"Applies to ePub, MOBI output formats")),
Option('--note-tag', Option('--note-tag',
default='*', default='*',
dest='note_tag', dest='note_tag',
@ -845,6 +869,7 @@ class EPUB_MOBI(CatalogPlugin):
catalog.copyResources() catalog.copyResources()
catalog.buildSources() catalog.buildSources()
''' '''
# A single number creates 'Last x days' only. # A single number creates 'Last x days' only.
# Multiple numbers create 'Last x days', 'x to y days ago' ... # Multiple numbers create 'Last x days', 'x to y days ago' ...
# e.g, [7,15,30,60], [30] # e.g, [7,15,30,60], [30]
@ -889,6 +914,7 @@ class EPUB_MOBI(CatalogPlugin):
and self.generateForKindle \ and self.generateForKindle \
else False else False
self.__genres = None self.__genres = None
self.genres = []
self.__genre_tags_dict = None self.__genre_tags_dict = None
self.__htmlFileList = [] self.__htmlFileList = []
self.__markerTags = self.getMarkerTags() self.__markerTags = self.getMarkerTags()
@ -900,13 +926,15 @@ class EPUB_MOBI(CatalogPlugin):
self.__progressString = '' self.__progressString = ''
f, _, p = opts.read_book_marker.partition(':') f, _, p = opts.read_book_marker.partition(':')
self.__read_book_marker = {'field':f, 'pattern':p} self.__read_book_marker = {'field':f, 'pattern':p}
f, p, hr = self.opts.merge_comments.split(':')
self.__merge_comments = {'field':f, 'position':p, 'hr':hr}
self.__reporter = report_progress self.__reporter = report_progress
self.__stylesheet = stylesheet self.__stylesheet = stylesheet
self.__thumbs = None self.__thumbs = None
self.__thumbWidth = 0 self.__thumbWidth = 0
self.__thumbHeight = 0 self.__thumbHeight = 0
self.__title = opts.catalog_title self.__title = opts.catalog_title
self.__totalSteps = 11.0 self.__totalSteps = 8.0
self.__useSeriesPrefixInTitlesSection = False self.__useSeriesPrefixInTitlesSection = False
self.__verbose = opts.verbose self.__verbose = opts.verbose
@ -916,17 +944,36 @@ class EPUB_MOBI(CatalogPlugin):
self.__output_profile = profile self.__output_profile = profile
break break
# Confirm/create thumbs archive # Confirm/create thumbs archive.
if not os.path.exists(self.__cache_dir): if not os.path.exists(self.__cache_dir):
self.opts.log.info(" creating new thumb cache '%s'" % self.__cache_dir) self.opts.log.info(" creating new thumb cache '%s'" % self.__cache_dir)
os.makedirs(self.__cache_dir) os.makedirs(self.__cache_dir)
if not os.path.exists(self.__archive_path): if not os.path.exists(self.__archive_path):
self.opts.log.info(" creating thumbnail archive") self.opts.log.info(' creating thumbnail archive, thumb_width: %1.2f"' %
float(self.opts.thumb_width))
zfw = ZipFile(self.__archive_path, mode='w') zfw = ZipFile(self.__archive_path, mode='w')
zfw.writestr("Catalog Thumbs Archive",'') zfw.writestr("Catalog Thumbs Archive",'')
zfw.comment = "thumb_width: %1.2f" % float(self.opts.thumb_width)
zfw.close() zfw.close()
else: else:
self.opts.log.info(" existing thumb cache at '%s'" % self.__archive_path) with closing(ZipFile(self.__archive_path, mode='r')) as zfr:
try:
cached_thumb_width = float(zfr.comment[len('thumb_width: '):])
except:
cached_thumb_width = "0.0"
if float(cached_thumb_width) != float(self.opts.thumb_width):
self.opts.log.info(" invalidating cache at '%s'" % self.__archive_path)
self.opts.log.info(' thumb_width: %1.2f" => %1.2f"' %
(float(cached_thumb_width),float(self.opts.thumb_width)))
os.remove(self.__archive_path)
zfw = ZipFile(self.__archive_path, mode='w')
zfw.writestr("Catalog Thumbs Archive",'')
zfw.comment = "thumb_width: %1.2f" % float(self.opts.thumb_width)
zfw.close()
else:
self.opts.log.info(' existing thumb cache at %s, cached_thumb_width: %1.2f"' %
(self.__archive_path, float(cached_thumb_width)))
# Tweak build steps based on optional sections: 1 call for HTML, 1 for NCX # Tweak build steps based on optional sections: 1 call for HTML, 1 for NCX
if self.opts.generate_titles: if self.opts.generate_titles:
@ -937,6 +984,9 @@ class EPUB_MOBI(CatalogPlugin):
self.__totalSteps += 2 self.__totalSteps += 2
if self.opts.generate_series: if self.opts.generate_series:
self.__totalSteps += 2 self.__totalSteps += 2
if self.opts.generate_descriptions:
# +1 thumbs
self.__totalSteps += 3
# Accessors # Accessors
if True: if True:
@ -1246,7 +1296,8 @@ class EPUB_MOBI(CatalogPlugin):
return False return False
self.fetchBooksByAuthor() self.fetchBooksByAuthor()
self.fetchBookmarks() self.fetchBookmarks()
self.generateHTMLDescriptions() if self.opts.generate_descriptions:
self.generateHTMLDescriptions()
self.generateHTMLByAuthor() self.generateHTMLByAuthor()
if self.opts.generate_titles: if self.opts.generate_titles:
self.generateHTMLByTitle() self.generateHTMLByTitle()
@ -1256,10 +1307,10 @@ class EPUB_MOBI(CatalogPlugin):
self.generateHTMLByDateAdded() self.generateHTMLByDateAdded()
if self.generateRecentlyRead: if self.generateRecentlyRead:
self.generateHTMLByDateRead() self.generateHTMLByDateRead()
self.generateHTMLByTags() if self.opts.generate_genres:
self.generateHTMLByTags()
self.generateThumbnails() if self.opts.generate_descriptions:
self.generateThumbnails()
self.generateOPF() self.generateOPF()
self.generateNCXHeader() self.generateNCXHeader()
self.generateNCXByAuthor("Authors") self.generateNCXByAuthor("Authors")
@ -1271,8 +1322,11 @@ class EPUB_MOBI(CatalogPlugin):
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") if self.opts.generate_genres:
self.generateNCXDescriptions("Descriptions") self.generateNCXByGenre("Genres")
if self.opts.generate_descriptions:
self.generateNCXDescriptions("Descriptions")
self.writeNCX() self.writeNCX()
return True return True
@ -1340,6 +1394,7 @@ class EPUB_MOBI(CatalogPlugin):
#print "fetchBooksByTitle(): opts.search_text: %s" % self.opts.search_text #print "fetchBooksByTitle(): opts.search_text: %s" % self.opts.search_text
# Fetch the database as a dictionary # Fetch the database as a dictionary
data = self.plugin.search_sort_db(self.db, self.opts) data = self.plugin.search_sort_db(self.db, self.opts)
data = self.processExclusions(data)
# Populate this_title{} from data[{},{}] # Populate this_title{} from data[{},{}]
titles = [] titles = []
@ -1388,6 +1443,8 @@ class EPUB_MOBI(CatalogPlugin):
record['comments'] = record['comments'][:ad_offset] record['comments'] = record['comments'][:ad_offset]
this_title['description'] = self.markdownComments(record['comments']) this_title['description'] = self.markdownComments(record['comments'])
# Create short description
paras = BeautifulSoup(this_title['description']).findAll('p') paras = BeautifulSoup(this_title['description']).findAll('p')
tokens = [] tokens = []
for p in paras: for p in paras:
@ -1399,6 +1456,10 @@ class EPUB_MOBI(CatalogPlugin):
this_title['description'] = None this_title['description'] = None
this_title['short_description'] = None this_title['short_description'] = None
# Merge with custom field/value
if self.__merge_comments['field']:
this_title['description'] = self.mergeComments(this_title)
if record['cover']: if record['cover']:
this_title['cover'] = re.sub('&amp;', '&', record['cover']) this_title['cover'] = re.sub('&amp;', '&', record['cover'])
@ -1413,6 +1474,14 @@ class EPUB_MOBI(CatalogPlugin):
formats.append(self.convertHTMLEntities(format)) formats.append(self.convertHTMLEntities(format))
this_title['formats'] = formats this_title['formats'] = formats
# Add user notes to be displayed in header
if self.opts.header_note_source_field:
notes = self.__db.get_field(record['id'],
self.opts.header_note_source_field,
index_is_id=True)
if notes:
this_title['notes'] = notes
titles.append(this_title) titles.append(this_title)
# Re-sort based on title_sort # Re-sort based on title_sort
@ -1712,7 +1781,8 @@ class EPUB_MOBI(CatalogPlugin):
for tag in title.get('tags', []): for tag in title.get('tags', []):
aTag = Tag(soup,'a') aTag = Tag(soup,'a')
#print "aTag: %s" % "Genre_%s.html" % re.sub("\W","",tag.lower()) #print "aTag: %s" % "Genre_%s.html" % re.sub("\W","",tag.lower())
aTag['href'] = "Genre_%s.html" % re.sub("\W","",tag.lower()) if self.opts.generate_genres:
aTag['href'] = "Genre_%s.html" % re.sub("\W","",tag.lower())
aTag.insert(0,escape(NavigableString(tag))) aTag.insert(0,escape(NavigableString(tag)))
emTag = Tag(soup, "em") emTag = Tag(soup, "em")
emTag.insert(0, aTag) emTag.insert(0, aTag)
@ -1771,7 +1841,6 @@ class EPUB_MOBI(CatalogPlugin):
#ratingLabel = body.find('td',text="Rating").replaceWith("Unrated") #ratingLabel = body.find('td',text="Rating").replaceWith("Unrated")
ratingTag.insert(0,NavigableString('<br/>')) ratingTag.insert(0,NavigableString('<br/>'))
# Insert user notes or remove Notes label. Notes > 1 line will push formatting down # Insert user notes or remove Notes label. Notes > 1 line will push formatting down
if 'notes' in title: if 'notes' in title:
notesTag = body.find(attrs={'class':'notes'}) notesTag = body.find(attrs={'class':'notes'})
@ -1894,7 +1963,8 @@ class EPUB_MOBI(CatalogPlugin):
# Link to book # Link to book
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id']))) if self.opts.generate_descriptions:
aTag['href'] = "book_%d.html" % (int(float(book['id'])))
aTag.insert(0,escape(book['title'])) aTag.insert(0,escape(book['title']))
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
@ -2067,8 +2137,9 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id']))) if self.opts.generate_descriptions:
# Use series, series index if avail else just title, + year of publication aTag['href'] = "book_%d.html" % (int(float(book['id'])))
# Use series, series index if avail else title, + year of publication
if current_series: if current_series:
aTag.insert(0,'%s (%s)' % (escape(book['title'][len(book['series'])+1:]), aTag.insert(0,'%s (%s)' % (escape(book['title'][len(book['series'])+1:]),
book['date'].split()[1])) book['date'].split()[1]))
@ -2079,7 +2150,6 @@ class EPUB_MOBI(CatalogPlugin):
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
divTag.insert(dtc, pBookTag) divTag.insert(dtc, pBookTag)
dtc += 1 dtc += 1
@ -2200,7 +2270,8 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) if self.opts.generate_descriptions:
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
if current_series: if current_series:
aTag.insert(0,escape(new_entry['title'][len(new_entry['series'])+1:])) aTag.insert(0,escape(new_entry['title'][len(new_entry['series'])+1:]))
else: else:
@ -2251,7 +2322,8 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) if self.opts.generate_descriptions:
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
aTag.insert(0,escape(new_entry['title'])) aTag.insert(0,escape(new_entry['title']))
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
@ -2411,7 +2483,8 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) if self.opts.generate_descriptions:
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
aTag.insert(0,escape(new_entry['title'])) aTag.insert(0,escape(new_entry['title']))
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
@ -2458,7 +2531,8 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(new_entry['id']))) if self.opts.generate_descriptions:
aTag['href'] = "book_%d.html" % (int(float(new_entry['id'])))
aTag.insert(0,escape(new_entry['title'])) aTag.insert(0,escape(new_entry['title']))
pBookTag.insert(ptc, aTag) pBookTag.insert(ptc, aTag)
ptc += 1 ptc += 1
@ -2699,7 +2773,8 @@ class EPUB_MOBI(CatalogPlugin):
ptc += 1 ptc += 1
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id']))) if self.opts.generate_descriptions:
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
#aTag.insert(0,'%d. %s &middot; %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors']))) #aTag.insert(0,'%d. %s &middot; %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors'])))
@ -2983,18 +3058,20 @@ class EPUB_MOBI(CatalogPlugin):
manifest.insert(mtc, itemTag) manifest.insert(mtc, itemTag)
mtc += 1 mtc += 1
# Write the thumbnail images to the manifest # Write the thumbnail images, descriptions to the manifest
for thumb in self.thumbs: sort_descriptions_by = []
itemTag = Tag(soup, "item") if self.opts.generate_descriptions:
itemTag['href'] = "images/%s" % (thumb) for thumb in self.thumbs:
end = thumb.find('.jpg') itemTag = Tag(soup, "item")
itemTag['id'] = "%s-image" % thumb[:end] itemTag['href'] = "images/%s" % (thumb)
itemTag['media-type'] = 'image/jpeg' end = thumb.find('.jpg')
manifest.insert(mtc, itemTag) itemTag['id'] = "%s-image" % thumb[:end]
mtc += 1 itemTag['media-type'] = 'image/jpeg'
manifest.insert(mtc, itemTag)
mtc += 1
# HTML files - add books to manifest and spine # HTML files - add descriptions 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
# Add html_files to manifest and spine # Add html_files to manifest and spine
@ -3970,15 +4047,15 @@ class EPUB_MOBI(CatalogPlugin):
from calibre.customize.ui import output_profiles from calibre.customize.ui import output_profiles
for x in output_profiles(): for x in output_profiles():
if x.short_name == self.opts.output_profile: if x.short_name == self.opts.output_profile:
# .9" width aspect ratio: 3:4 # aspect ratio: 3:4
self.thumbWidth = int(x.dpi * 1) self.thumbWidth = x.dpi * float(self.opts.thumb_width)
self.thumbHeight = int(self.thumbWidth * 1.33) self.thumbHeight = self.thumbWidth * 1.33
if 'kindle' in x.short_name and self.opts.fmt == 'mobi': if 'kindle' in x.short_name and self.opts.fmt == 'mobi':
# Kindle DPI appears to be off by a factor of 2 # Kindle DPI appears to be off by a factor of 2
self.thumbWidth = int(self.thumbWidth/2) self.thumbWidth = self.thumbWidth/2
self.thumbHeight = int(self.thumbHeight/2) self.thumbHeight = self.thumbHeight/2
break break
if False and self.verbose: if True and self.verbose:
self.opts.log(" DPI = %d; thumbnail dimensions: %d x %d" % \ self.opts.log(" DPI = %d; thumbnail dimensions: %d x %d" % \
(x.dpi, self.thumbWidth, self.thumbHeight)) (x.dpi, self.thumbWidth, self.thumbHeight))
@ -4238,7 +4315,8 @@ class EPUB_MOBI(CatalogPlugin):
# Add the book title # Add the book title
aTag = Tag(soup, "a") aTag = Tag(soup, "a")
aTag['href'] = "book_%d.html" % (int(float(book['id']))) if self.opts.generate_descriptions:
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
if current_series: if current_series:
aTag.insert(0,escape(book['title'][len(book['series'])+1:])) aTag.insert(0,escape(book['title'][len(book['series'])+1:]))
@ -4460,7 +4538,9 @@ class EPUB_MOBI(CatalogPlugin):
# Leading numbers optionally translated to text equivalent # Leading numbers optionally translated to text equivalent
# Capitalize leading sort word # Capitalize leading sort word
if i==0: if i==0:
if self.opts.numbers_as_text and re.match('[0-9]+',word[0]): # *** Keep this code in case we need to restore numbers_as_text ***
if False:
#if self.opts.numbers_as_text and re.match('[0-9]+',word[0]):
translated.append(EPUB_MOBI.NumberToText(word).text.capitalize()) translated.append(EPUB_MOBI.NumberToText(word).text.capitalize())
else: else:
if re.match('[0-9]+',word[0]): if re.match('[0-9]+',word[0]):
@ -4540,7 +4620,6 @@ class EPUB_MOBI(CatalogPlugin):
''' 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 = []
markerTags.extend(self.opts.exclude_tags.split(',')) markerTags.extend(self.opts.exclude_tags.split(','))
markerTags.extend(self.opts.note_tag.split(','))
return markerTags return markerTags
def letter_or_symbol(self,char): def letter_or_symbol(self,char):
@ -4663,13 +4742,63 @@ class EPUB_MOBI(CatalogPlugin):
return result.renderContents(encoding=None) return result.renderContents(encoding=None)
def mergeComments(self, record):
'''
merge ['description'] with custom field contents to be displayed in Descriptions
'''
merged = ''
if record['description']:
addendum = self.__db.get_field(record['id'],
self.__merge_comments['field'],
index_is_id=True)
include_hr = eval(self.__merge_comments['hr'])
if self.__merge_comments['position'] == 'before':
merged = addendum
if include_hr:
merged += '<hr class="merged_comments_divider"/>'
else:
merged += '\n'
merged += record['description']
else:
merged = record['description']
if include_hr:
merged += '<hr class="merged_comments_divider"/>'
else:
merged += '\n'
merged += addendum
else:
# Return the custom field contents
merged = self.__db.get_field(record['id'],
self.__merge_comments['field'],
index_is_id=True)
return merged
def processExclusions(self, data_set):
'''
Remove excluded entries
'''
field, pat = self.opts.exclude_book_marker.split(':')
if pat == '':
return data_set
filtered_data_set = []
for record in data_set:
field_contents = self.__db.get_field(record['id'],
field,
index_is_id=True)
if field_contents:
if re.search(pat, unicode(field_contents),
re.IGNORECASE) is not None:
continue
filtered_data_set.append(record)
return filtered_data_set
def processSpecialTags(self, tags, this_title, opts): def processSpecialTags(self, tags, this_title, opts):
tag_list = [] tag_list = []
for tag in tags: for tag in tags:
tag = self.convertHTMLEntities(tag) tag = self.convertHTMLEntities(tag)
if tag.startswith(opts.note_tag): if re.search(opts.exclude_genre, tag):
this_title['notes'] = tag[len(self.opts.note_tag):]
elif re.search(opts.exclude_genre, tag):
continue continue
elif self.__read_book_marker['field'] == 'tag' and \ elif self.__read_book_marker['field'] == 'tag' and \
tag == self.__read_book_marker['pattern']: tag == self.__read_book_marker['pattern']:
@ -4767,13 +4896,16 @@ class EPUB_MOBI(CatalogPlugin):
if opts_dict['ids']: if opts_dict['ids']:
build_log.append(" book count: %d" % len(opts_dict['ids'])) build_log.append(" book count: %d" % len(opts_dict['ids']))
sections_list = ['Descriptions','Authors'] sections_list = ['Authors']
if opts.generate_titles: if opts.generate_titles:
sections_list.append('Titles') sections_list.append('Titles')
if opts.generate_recently_added: if opts.generate_recently_added:
sections_list.append('Recently Added') sections_list.append('Recently Added')
if not opts.exclude_genre.strip() == '.': if opts.generate_genres:
sections_list.append('Genres') sections_list.append('Genres')
if opts.generate_descriptions:
sections_list.append('Descriptions')
build_log.append(u" Sections: %s" % ', '.join(sections_list)) build_log.append(u" Sections: %s" % ', '.join(sections_list))
# Display opts # Display opts
@ -4782,11 +4914,12 @@ class EPUB_MOBI(CatalogPlugin):
build_log.append(" opts:") build_log.append(" opts:")
for key in keys: for key in keys:
if key in ['catalog_title','authorClip','connected_kindle','descriptionClip', if key in ['catalog_title','authorClip','connected_kindle','descriptionClip',
'exclude_genre','exclude_tags','note_tag','numbers_as_text', 'exclude_book_marker','exclude_genre','exclude_tags',
'header_note_source_field','merge_comments',
'output_profile','read_book_marker', 'output_profile','read_book_marker',
'search_text','sort_by','sort_descriptions_by_author','sync', 'search_text','sort_by','sort_descriptions_by_author','sync',
'wishlist_tag']: 'thumb_width','wishlist_tag']:
build_log.append(" %s: %s" % (key, opts_dict[key])) build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
if opts.verbose: if opts.verbose:
log('\n'.join(line for line in build_log)) log('\n'.join(line for line in build_log))
@ -4801,6 +4934,7 @@ class EPUB_MOBI(CatalogPlugin):
catalog.copyResources() catalog.copyResources()
catalog.calculateThumbnailSize() catalog.calculateThumbnailSize()
catalog_source_built = catalog.buildSources() catalog_source_built = catalog.buildSources()
if opts.verbose: if opts.verbose:
if catalog_source_built: if catalog_source_built:
log.info(" Completed catalog source generation\n") log.info(" Completed catalog source generation\n")