mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
EPUB/MOBI Catalogs: Allow saving used settings as presets which can be loaded easily later. Fixes #1155587 ([EHANCEMENT] Save named catalog parameters)
This commit is contained in:
commit
9d90ba326d
@ -12,20 +12,22 @@ from functools import partial
|
|||||||
|
|
||||||
from calibre.ebooks.conversion.config import load_defaults
|
from calibre.ebooks.conversion.config import load_defaults
|
||||||
from calibre.gui2 import gprefs, open_url, question_dialog
|
from calibre.gui2 import gprefs, open_url, question_dialog
|
||||||
|
from calibre.utils.config import JSONConfig
|
||||||
from calibre.utils.icu import sort_key
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
from catalog_epub_mobi_ui import Ui_Form
|
from catalog_epub_mobi_ui import Ui_Form
|
||||||
|
from PyQt4 import QtGui
|
||||||
from PyQt4.Qt import (Qt, QAbstractItemView, QCheckBox, QComboBox,
|
from PyQt4.Qt import (Qt, QAbstractItemView, QCheckBox, QComboBox,
|
||||||
QDoubleSpinBox, QIcon, QLineEdit, QObject, QRadioButton, QSize, QSizePolicy,
|
QDoubleSpinBox, QIcon, QInputDialog, QLineEdit, QObject, QRadioButton,
|
||||||
QTableWidget, QTableWidgetItem, QTextEdit, QToolButton, QUrl,
|
QSize, QSizePolicy, QTableWidget, QTableWidgetItem, QTextEdit, QToolButton,
|
||||||
QVBoxLayout, QWidget,
|
QUrl, QVBoxLayout, QWidget,
|
||||||
SIGNAL)
|
SIGNAL)
|
||||||
|
|
||||||
class PluginWidget(QWidget,Ui_Form):
|
class PluginWidget(QWidget,Ui_Form):
|
||||||
|
|
||||||
TITLE = _('E-book options')
|
TITLE = _('E-book options')
|
||||||
HELP = _('Options specific to')+' AZW3/EPUB/MOBI '+_('output')
|
HELP = _('Options specific to')+' AZW3/EPUB/MOBI '+_('output')
|
||||||
DEBUG = False
|
DEBUG = True
|
||||||
|
|
||||||
# Output synced to the connected device?
|
# Output synced to the connected device?
|
||||||
sync_enabled = True
|
sync_enabled = True
|
||||||
@ -212,8 +214,8 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
else:
|
else:
|
||||||
results = _truncated_results(excluded_tags)
|
results = _truncated_results(excluded_tags)
|
||||||
finally:
|
finally:
|
||||||
if self.DEBUG:
|
if False and self.DEBUG:
|
||||||
print(results)
|
print("exclude_genre_changed(): %s" % results)
|
||||||
self.exclude_genre_results.clear()
|
self.exclude_genre_results.clear()
|
||||||
self.exclude_genre_results.setText(results)
|
self.exclude_genre_results.setText(results)
|
||||||
|
|
||||||
@ -239,11 +241,11 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
Toggle Description-related controls
|
Toggle Description-related controls
|
||||||
'''
|
'''
|
||||||
self.header_note_source_field.setEnabled(enabled)
|
self.header_note_source_field.setEnabled(enabled)
|
||||||
self.thumb_width.setEnabled(enabled)
|
|
||||||
self.merge_source_field.setEnabled(enabled)
|
|
||||||
self.merge_before.setEnabled(enabled)
|
|
||||||
self.merge_after.setEnabled(enabled)
|
|
||||||
self.include_hr.setEnabled(enabled)
|
self.include_hr.setEnabled(enabled)
|
||||||
|
self.merge_after.setEnabled(enabled)
|
||||||
|
self.merge_before.setEnabled(enabled)
|
||||||
|
self.merge_source_field.setEnabled(enabled)
|
||||||
|
self.thumb_width.setEnabled(enabled)
|
||||||
|
|
||||||
def generate_genres_changed(self, enabled):
|
def generate_genres_changed(self, enabled):
|
||||||
'''
|
'''
|
||||||
@ -263,6 +265,22 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
self.genre_source_field_name = genre_source_spec['field']
|
self.genre_source_field_name = genre_source_spec['field']
|
||||||
self.exclude_genre_changed()
|
self.exclude_genre_changed()
|
||||||
|
|
||||||
|
def get_format_and_title(self):
|
||||||
|
current_format = None
|
||||||
|
current_title = None
|
||||||
|
self.parentWidget().blockSignals(True)
|
||||||
|
for peer in self.parentWidget().children():
|
||||||
|
if peer == self:
|
||||||
|
continue
|
||||||
|
elif peer.children():
|
||||||
|
for child in peer.children():
|
||||||
|
if child.objectName() == 'format':
|
||||||
|
current_format = str(child.currentText()).strip()
|
||||||
|
elif child.objectName() == 'title':
|
||||||
|
current_title = str(child.text()).strip()
|
||||||
|
self.parentWidget().blockSignals(False)
|
||||||
|
return current_format, current_title
|
||||||
|
|
||||||
def header_note_source_field_changed(self,new_index):
|
def header_note_source_field_changed(self,new_index):
|
||||||
'''
|
'''
|
||||||
Process changes in the header_note_source_field combo box
|
Process changes in the header_note_source_field combo box
|
||||||
@ -383,6 +401,11 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
# Initialize excluded genres preview
|
# Initialize excluded genres preview
|
||||||
self.exclude_genre_changed()
|
self.exclude_genre_changed()
|
||||||
|
|
||||||
|
# Hook Preset signals
|
||||||
|
self.preset_delete_pb.clicked.connect(self.preset_remove)
|
||||||
|
self.preset_save_pb.clicked.connect(self.preset_save)
|
||||||
|
self.preset_field.currentIndexChanged[str].connect(self.preset_change)
|
||||||
|
|
||||||
def merge_source_field_changed(self,new_index):
|
def merge_source_field_changed(self,new_index):
|
||||||
'''
|
'''
|
||||||
Process changes in the merge_source_field combo box
|
Process changes in the merge_source_field combo box
|
||||||
@ -404,10 +427,12 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
self.include_hr.setEnabled(False)
|
self.include_hr.setEnabled(False)
|
||||||
|
|
||||||
def options(self):
|
def options(self):
|
||||||
# Save/return the current options
|
'''
|
||||||
# exclude_genre stores literally
|
Return, optionally save current options
|
||||||
# Section switches store as True/False
|
exclude_genre stores literally
|
||||||
# others store as lists
|
Section switches store as True/False
|
||||||
|
others store as lists
|
||||||
|
'''
|
||||||
|
|
||||||
opts_dict = {}
|
opts_dict = {}
|
||||||
prefix_rules_processed = False
|
prefix_rules_processed = False
|
||||||
@ -469,7 +494,7 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
except:
|
except:
|
||||||
opts_dict['output_profile'] = ['default']
|
opts_dict['output_profile'] = ['default']
|
||||||
|
|
||||||
if self.DEBUG:
|
if False and self.DEBUG:
|
||||||
print "opts_dict"
|
print "opts_dict"
|
||||||
for opt in sorted(opts_dict.keys(), key=sort_key):
|
for opt in sorted(opts_dict.keys(), key=sort_key):
|
||||||
print " %s: %s" % (opt, repr(opts_dict[opt]))
|
print " %s: %s" % (opt, repr(opts_dict[opt]))
|
||||||
@ -544,6 +569,213 @@ class PluginWidget(QWidget,Ui_Form):
|
|||||||
self.genre_source_fields = custom_fields
|
self.genre_source_fields = custom_fields
|
||||||
self.genre_source_field.currentIndexChanged.connect(self.genre_source_field_changed)
|
self.genre_source_field.currentIndexChanged.connect(self.genre_source_field_changed)
|
||||||
|
|
||||||
|
# Populate the Presets combo box
|
||||||
|
self.presets = JSONConfig("catalog_presets")
|
||||||
|
self.preset_field.addItem("")
|
||||||
|
self.preset_field_values = sorted([p for p in self.presets], key=sort_key)
|
||||||
|
self.preset_field.addItems(self.preset_field_values)
|
||||||
|
|
||||||
|
def preset_change(self, item_name):
|
||||||
|
'''
|
||||||
|
Update catalog options from current preset
|
||||||
|
'''
|
||||||
|
if not item_name:
|
||||||
|
return
|
||||||
|
|
||||||
|
current_preset = str(self.preset_field.currentText())
|
||||||
|
options = self.presets[current_preset]
|
||||||
|
|
||||||
|
exclusion_rules = []
|
||||||
|
prefix_rules = []
|
||||||
|
for opt in self.OPTION_FIELDS:
|
||||||
|
c_name, c_def, c_type = opt
|
||||||
|
if c_name == 'preset_field':
|
||||||
|
continue
|
||||||
|
# Ignore extra entries in options for cli invocation
|
||||||
|
if c_name in options:
|
||||||
|
opt_value = options[c_name]
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
if c_type in ['check_box']:
|
||||||
|
getattr(self, c_name).setChecked(eval(str(opt_value)))
|
||||||
|
if c_name == 'generate_genres':
|
||||||
|
self.genre_source_field.setEnabled(eval(str(opt_value)))
|
||||||
|
elif c_type in ['combo_box']:
|
||||||
|
if opt_value is None:
|
||||||
|
index = 0
|
||||||
|
if c_name == 'genre_source_field':
|
||||||
|
index = self.genre_source_field.findText(_('Tags'))
|
||||||
|
else:
|
||||||
|
index = getattr(self,c_name).findText(opt_value)
|
||||||
|
if index == -1:
|
||||||
|
if c_name == 'read_source_field':
|
||||||
|
index = self.read_source_field.findText(_('Tags'))
|
||||||
|
elif c_name == 'genre_source_field':
|
||||||
|
index = self.genre_source_field.findText(_('Tags'))
|
||||||
|
getattr(self,c_name).setCurrentIndex(index)
|
||||||
|
elif c_type in ['line_edit']:
|
||||||
|
getattr(self, c_name).setText(opt_value if opt_value else '')
|
||||||
|
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))
|
||||||
|
if c_type == 'table_widget':
|
||||||
|
if c_name == 'exclusion_rules_tw':
|
||||||
|
if opt_value not in exclusion_rules:
|
||||||
|
exclusion_rules.append(opt_value)
|
||||||
|
if c_name == 'prefix_rules_tw':
|
||||||
|
if opt_value not in prefix_rules:
|
||||||
|
prefix_rules.append(opt_value)
|
||||||
|
|
||||||
|
# Reset exclusion rules
|
||||||
|
self.exclusion_rules_table.clearLayout()
|
||||||
|
self.exclusion_rules_table = ExclusionRules(self.exclusion_rules_gb,
|
||||||
|
"exclusion_rules_tw", exclusion_rules, self.eligible_custom_fields, self.db)
|
||||||
|
|
||||||
|
# Reset prefix rules
|
||||||
|
self.prefix_rules_table.clearLayout()
|
||||||
|
self.prefix_rules_table = PrefixRules(self.prefix_rules_gb,
|
||||||
|
"prefix_rules_tw", prefix_rules, self.eligible_custom_fields, self.db)
|
||||||
|
|
||||||
|
# Reset excluded genres preview
|
||||||
|
self.exclude_genre_changed()
|
||||||
|
|
||||||
|
# Reset format and title
|
||||||
|
format = options['format']
|
||||||
|
title = options['catalog_title']
|
||||||
|
self.set_format_and_title(format, title)
|
||||||
|
|
||||||
|
def preset_remove(self):
|
||||||
|
if self.preset_field.currentIndex() == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not question_dialog(self, _("Delete saved catalog preset"),
|
||||||
|
_("The selected saved catalog preset will be deleted. "
|
||||||
|
"Are you sure?")):
|
||||||
|
return
|
||||||
|
|
||||||
|
item_id = self.preset_field.currentIndex()
|
||||||
|
item_name = unicode(self.preset_field.currentText())
|
||||||
|
|
||||||
|
self.preset_field.blockSignals(True)
|
||||||
|
self.preset_field.removeItem(item_id)
|
||||||
|
self.preset_field.blockSignals(False)
|
||||||
|
self.preset_field.setCurrentIndex(0)
|
||||||
|
|
||||||
|
if item_name in self.presets.keys():
|
||||||
|
del(self.presets[item_name])
|
||||||
|
self.presets.commit()
|
||||||
|
|
||||||
|
def preset_save(self):
|
||||||
|
names = ['']
|
||||||
|
names.extend(self.preset_field_values)
|
||||||
|
try:
|
||||||
|
dex = names.index(self.preset_search_name)
|
||||||
|
except:
|
||||||
|
dex = 0
|
||||||
|
name = ''
|
||||||
|
while not name:
|
||||||
|
name, ok = QInputDialog.getItem(self, _('Save catalog preset'),
|
||||||
|
_('Preset name:'), names, dex, True)
|
||||||
|
if not ok:
|
||||||
|
return
|
||||||
|
if not name:
|
||||||
|
error_dialog(self, _("Save catalog preset"),
|
||||||
|
_("You must provide a name."), show=True)
|
||||||
|
new = True
|
||||||
|
name = unicode(name)
|
||||||
|
if name in self.presets.keys():
|
||||||
|
if not question_dialog(self, _("Save catalog preset"),
|
||||||
|
_("That saved preset already exists and will be overwritten. "
|
||||||
|
"Are you sure?")):
|
||||||
|
return
|
||||||
|
new = False
|
||||||
|
|
||||||
|
preset = {}
|
||||||
|
prefix_rules_processed = False
|
||||||
|
exclusion_rules_processed = False
|
||||||
|
|
||||||
|
for opt in self.OPTION_FIELDS:
|
||||||
|
c_name, c_def, c_type = opt
|
||||||
|
if c_name == 'exclusion_rules_tw' and exclusion_rules_processed:
|
||||||
|
continue
|
||||||
|
if c_name == 'prefix_rules_tw' and prefix_rules_processed:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if c_type in ['check_box', 'radio_button']:
|
||||||
|
opt_value = getattr(self, c_name).isChecked()
|
||||||
|
elif c_type in ['combo_box']:
|
||||||
|
if c_name == 'preset_field':
|
||||||
|
continue
|
||||||
|
opt_value = unicode(getattr(self,c_name).currentText()).strip()
|
||||||
|
elif c_type in ['line_edit']:
|
||||||
|
opt_value = unicode(getattr(self, c_name).text()).strip()
|
||||||
|
elif c_type in ['spin_box']:
|
||||||
|
opt_value = unicode(getattr(self, c_name).value())
|
||||||
|
elif c_type in ['table_widget']:
|
||||||
|
if c_name == 'prefix_rules_tw':
|
||||||
|
opt_value = self.prefix_rules_table.get_data()
|
||||||
|
prefix_rules_processed = True
|
||||||
|
if c_name == 'exclusion_rules_tw':
|
||||||
|
opt_value = self.exclusion_rules_table.get_data()
|
||||||
|
exclusion_rules_processed = True
|
||||||
|
|
||||||
|
preset[c_name] = opt_value
|
||||||
|
# Construct cli version of table rules
|
||||||
|
if c_name in ['exclusion_rules_tw','prefix_rules_tw']:
|
||||||
|
self.construct_tw_opts_object(c_name, opt_value, preset)
|
||||||
|
|
||||||
|
format, title = self.get_format_and_title()
|
||||||
|
preset['format'] = format
|
||||||
|
preset['catalog_title'] = title
|
||||||
|
|
||||||
|
# Additional items needed for cli invocation
|
||||||
|
# Generate specs for merge_comments, header_note_source_field, genre_source_field
|
||||||
|
checked = ''
|
||||||
|
if self.merge_before.isChecked():
|
||||||
|
checked = 'before'
|
||||||
|
elif self.merge_after.isChecked():
|
||||||
|
checked = 'after'
|
||||||
|
include_hr = self.include_hr.isChecked()
|
||||||
|
preset['merge_comments_rule'] = "%s:%s:%s" % \
|
||||||
|
(self.merge_source_field_name, checked, include_hr)
|
||||||
|
|
||||||
|
preset['header_note_source_field'] = self.header_note_source_field_name
|
||||||
|
|
||||||
|
preset['genre_source_field'] = self.genre_source_field_name
|
||||||
|
|
||||||
|
# Append the current output profile
|
||||||
|
try:
|
||||||
|
preset['output_profile'] = load_defaults('page_setup')['output_profile']
|
||||||
|
except:
|
||||||
|
preset['output_profile'] = 'default'
|
||||||
|
|
||||||
|
self.presets[name] = preset
|
||||||
|
self.presets.commit()
|
||||||
|
|
||||||
|
if new:
|
||||||
|
self.preset_field.blockSignals(True)
|
||||||
|
self.preset_field.clear()
|
||||||
|
self.preset_field.addItem('')
|
||||||
|
self.preset_field_values = sorted([q for q in self.presets], key=sort_key)
|
||||||
|
self.preset_field.addItems(self.preset_field_values)
|
||||||
|
self.preset_field.blockSignals(False)
|
||||||
|
self.preset_field.setCurrentIndex(self.preset_field.findText(name))
|
||||||
|
|
||||||
|
def set_format_and_title(self, format, title):
|
||||||
|
for peer in self.parentWidget().children():
|
||||||
|
if peer == self:
|
||||||
|
continue
|
||||||
|
elif peer.children():
|
||||||
|
for child in peer.children():
|
||||||
|
if child.objectName() == 'format':
|
||||||
|
index = child.findText(format)
|
||||||
|
child.blockSignals(True)
|
||||||
|
child.setCurrentIndex(index)
|
||||||
|
child.blockSignals(False)
|
||||||
|
elif child.objectName() == 'title':
|
||||||
|
child.setText(title)
|
||||||
|
|
||||||
def show_help(self):
|
def show_help(self):
|
||||||
'''
|
'''
|
||||||
Display help file
|
Display help file
|
||||||
@ -631,6 +863,7 @@ class GenericRulesTable(QTableWidget):
|
|||||||
self.last_row_selected = self.currentRow()
|
self.last_row_selected = self.currentRow()
|
||||||
self.last_rows_selected = self.selectionModel().selectedRows()
|
self.last_rows_selected = self.selectionModel().selectedRows()
|
||||||
|
|
||||||
|
# Add the controls
|
||||||
self._init_controls()
|
self._init_controls()
|
||||||
|
|
||||||
# Hook check_box changes
|
# Hook check_box changes
|
||||||
@ -681,6 +914,21 @@ class GenericRulesTable(QTableWidget):
|
|||||||
# In case table was empty
|
# In case table was empty
|
||||||
self.horizontalHeader().setStretchLastSection(True)
|
self.horizontalHeader().setStretchLastSection(True)
|
||||||
|
|
||||||
|
def clearLayout(self):
|
||||||
|
if self.layout is not None:
|
||||||
|
old_layout = self.layout
|
||||||
|
|
||||||
|
for child in old_layout.children():
|
||||||
|
for i in reversed(range(child.count())):
|
||||||
|
if child.itemAt(i).widget() is not None:
|
||||||
|
child.itemAt(i).widget().setParent(None)
|
||||||
|
import sip
|
||||||
|
sip.delete(child)
|
||||||
|
|
||||||
|
for i in reversed(range(old_layout.count())):
|
||||||
|
if old_layout.itemAt(i).widget() is not None:
|
||||||
|
old_layout.itemAt(i).widget().setParent(None)
|
||||||
|
|
||||||
def delete_row(self):
|
def delete_row(self):
|
||||||
if self.DEBUG:
|
if self.DEBUG:
|
||||||
print("%s:delete_row()" % self.objectName())
|
print("%s:delete_row()" % self.objectName())
|
||||||
|
@ -20,6 +20,54 @@
|
|||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="catalogPresets">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="title">
|
||||||
|
<string>Presets</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="preset_field">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Select catalog preset to load</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="preset_save_pb">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Save current catalog settings as preset</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="preset_delete_pb">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Delete current preset</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Delete</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="includedSections">
|
<widget class="QGroupBox" name="includedSections">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -46,6 +94,9 @@
|
|||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>List of books, sorted by Author</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Authors</string>
|
<string>&Authors</string>
|
||||||
</property>
|
</property>
|
||||||
@ -54,15 +105,21 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QCheckBox" name="generate_titles">
|
<widget class="QCheckBox" name="generate_titles">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>List of books, sorted by Title</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Titles</string>
|
<string>&Titles</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QCheckBox" name="generate_series">
|
<widget class="QCheckBox" name="generate_series">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>List of series books, sorted by Series</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Series</string>
|
<string>&Series</string>
|
||||||
</property>
|
</property>
|
||||||
@ -72,6 +129,9 @@
|
|||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="generate_genres">
|
<widget class="QCheckBox" name="generate_genres">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>List of books, sorted by Genre</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Genres</string>
|
<string>&Genres</string>
|
||||||
</property>
|
</property>
|
||||||
@ -80,13 +140,13 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QComboBox" name="genre_source_field">
|
<widget class="QComboBox" name="genre_source_field">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Field containing Genre information</string>
|
<string>Field containing Genres</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="2">
|
<item row="2" column="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="generate_recently_added">
|
<widget class="QCheckBox" name="generate_recently_added">
|
||||||
@ -96,6 +156,9 @@
|
|||||||
<height>26</height>
|
<height>26</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>List of books, sorted by date added to calibre</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Recently Added</string>
|
<string>&Recently Added</string>
|
||||||
</property>
|
</property>
|
||||||
@ -103,7 +166,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="2">
|
<item row="4" column="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="generate_descriptions">
|
<widget class="QCheckBox" name="generate_descriptions">
|
||||||
@ -113,6 +176,9 @@
|
|||||||
<height>26</height>
|
<height>26</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Individual descriptions of books with cover thumbs, sorted by author</string>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Descriptions</string>
|
<string>&Descriptions</string>
|
||||||
</property>
|
</property>
|
||||||
@ -120,6 +186,41 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="1" rowspan="5">
|
||||||
|
<widget class="Line" name="line_3">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="Line" name="line_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="Line" name="line_6">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="Line" name="line_7">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="2">
|
||||||
|
<widget class="Line" name="line_8">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -347,7 +448,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Custom column containing additional content to be merged with Comments metadata.</string>
|
<string>Custom column containing additional content to be merged with Comments metadata in Descriptions section.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -361,7 +462,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="merge_before">
|
<widget class="QRadioButton" name="merge_before">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Merge additional content before Comments metadata.</string>
|
<string>Merge additional content before Comments in Descriptions section.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Before</string>
|
<string>&Before</string>
|
||||||
@ -374,7 +475,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="merge_after">
|
<widget class="QRadioButton" name="merge_after">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Merge additional content after Comments metadata.</string>
|
<string>Merge additional content after Comments in Descriptions section.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&After</string>
|
<string>&After</string>
|
||||||
@ -394,7 +495,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="include_hr">
|
<widget class="QCheckBox" name="include_hr">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Separate Comments metadata and additional content with a horizontal rule.</string>
|
<string>Separate Comments metadata and additional content with a horizontal rule in Descriptions section.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Include &Separator</string>
|
<string>Include &Separator</string>
|
||||||
@ -514,7 +615,7 @@ The default pattern \[.+\]|\+ excludes tags of the form [tag], e.g., [Test book]
|
|||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Custom column source for text to include in Description section.</string>
|
<string>Custom column source for text to include in Descriptions section.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -11,17 +11,18 @@ import os
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
from calibre import strftime
|
from calibre import strftime
|
||||||
|
from calibre.constants import config_dir
|
||||||
from calibre.customize import CatalogPlugin
|
from calibre.customize import CatalogPlugin
|
||||||
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
from calibre.customize.conversion import OptionRecommendation, DummyReporter
|
||||||
from calibre.ebooks import calibre_cover
|
from calibre.ebooks import calibre_cover
|
||||||
from calibre.library import current_library_name
|
from calibre.library import current_library_name
|
||||||
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
|
from calibre.library.catalogs import AuthorSortMismatchException, EmptyCatalogException
|
||||||
from calibre.ptempfile import PersistentTemporaryFile
|
from calibre.ptempfile import PersistentTemporaryFile
|
||||||
|
from calibre.utils.config import JSONConfig
|
||||||
from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang, get_lang
|
from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang, get_lang
|
||||||
|
|
||||||
Option = namedtuple('Option', 'option, default, dest, action, help')
|
Option = namedtuple('Option', 'option, default, dest, action, help')
|
||||||
|
|
||||||
|
|
||||||
class EPUB_MOBI(CatalogPlugin):
|
class EPUB_MOBI(CatalogPlugin):
|
||||||
'ePub catalog generator'
|
'ePub catalog generator'
|
||||||
|
|
||||||
@ -162,6 +163,14 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
"When multiple rules are defined, the first matching rule will be used.\n"
|
"When multiple rules are defined, the first matching rule will be used.\n"
|
||||||
"Default:\n" + '"' + '%default' + '"' + "\n"
|
"Default:\n" + '"' + '%default' + '"' + "\n"
|
||||||
"Applies to AZW3, ePub, MOBI output formats")),
|
"Applies to AZW3, ePub, MOBI output formats")),
|
||||||
|
Option('--preset',
|
||||||
|
default=None,
|
||||||
|
dest='preset',
|
||||||
|
action=None,
|
||||||
|
help=_("Use a named preset created with the GUI Catalog builder.\n"
|
||||||
|
"A preset specifies all settings for building a catalog.\n"
|
||||||
|
"Default: '%default'\n"
|
||||||
|
"Applies to AZW3, ePub, MOBI output formats")),
|
||||||
Option('--use-existing-cover',
|
Option('--use-existing-cover',
|
||||||
default=False,
|
default=False,
|
||||||
dest='use_existing_cover',
|
dest='use_existing_cover',
|
||||||
@ -184,6 +193,43 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder
|
from calibre.library.catalogs.epub_mobi_builder import CatalogBuilder
|
||||||
from calibre.utils.logging import default_log as log
|
from calibre.utils.logging import default_log as log
|
||||||
|
|
||||||
|
# If preset specified from the cli, insert stored options from JSON file
|
||||||
|
if hasattr(opts, 'preset') and opts.preset:
|
||||||
|
available_presets = JSONConfig("catalog_presets")
|
||||||
|
if not opts.preset in available_presets:
|
||||||
|
if available_presets:
|
||||||
|
print(_('Error: Preset "%s" not found.' % opts.preset))
|
||||||
|
print(_('Stored presets: %s' % ', '.join([p for p in sorted(available_presets.keys())])))
|
||||||
|
else:
|
||||||
|
print(_('Error: No stored presets.'))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Copy the relevant preset values to the opts object
|
||||||
|
for item in available_presets[opts.preset]:
|
||||||
|
if not item in ['exclusion_rules_tw', 'format', 'prefix_rules_tw']:
|
||||||
|
setattr(opts, item, available_presets[opts.preset][item])
|
||||||
|
|
||||||
|
# Provide an unconnected device
|
||||||
|
opts.connected_device = {
|
||||||
|
'is_device_connected': False,
|
||||||
|
'kind': None,
|
||||||
|
'name': None,
|
||||||
|
'save_template': None,
|
||||||
|
'serial': None,
|
||||||
|
'storage': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert prefix_rules and exclusion_rules from JSON lists to tuples
|
||||||
|
prs = []
|
||||||
|
for rule in opts.prefix_rules:
|
||||||
|
prs.append(tuple(rule))
|
||||||
|
opts.prefix_rules = tuple(prs)
|
||||||
|
|
||||||
|
ers = []
|
||||||
|
for rule in opts.exclusion_rules:
|
||||||
|
ers.append(tuple(rule))
|
||||||
|
opts.exclusion_rules = tuple(ers)
|
||||||
|
|
||||||
opts.log = log
|
opts.log = log
|
||||||
opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
|
opts.fmt = self.fmt = path_to_output.rpartition('.')[2]
|
||||||
|
|
||||||
@ -329,15 +375,14 @@ class EPUB_MOBI(CatalogPlugin):
|
|||||||
log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule))
|
log.error("incorrect number of args for --exclusion-rules: %s" % repr(rule))
|
||||||
|
|
||||||
# Display opts
|
# Display opts
|
||||||
keys = opts_dict.keys()
|
keys = sorted(opts_dict.keys())
|
||||||
keys.sort()
|
|
||||||
build_log.append(" opts:")
|
build_log.append(" opts:")
|
||||||
for key in keys:
|
for key in keys:
|
||||||
if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator',
|
if key in ['catalog_title', 'author_clip', 'connected_kindle', 'creator',
|
||||||
'cross_reference_authors', 'description_clip', 'exclude_book_marker',
|
'cross_reference_authors', 'description_clip', 'exclude_book_marker',
|
||||||
'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt',
|
'exclude_genre', 'exclude_tags', 'exclusion_rules', 'fmt',
|
||||||
'genre_source_field', 'header_note_source_field', 'merge_comments_rule',
|
'genre_source_field', 'header_note_source_field', 'merge_comments_rule',
|
||||||
'output_profile', 'prefix_rules', 'read_book_marker',
|
'output_profile', 'prefix_rules', 'preset', 'read_book_marker',
|
||||||
'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync',
|
'search_text', 'sort_by', 'sort_descriptions_by_author', 'sync',
|
||||||
'thumb_width', 'use_existing_cover', 'wishlist_tag']:
|
'thumb_width', 'use_existing_cover', 'wishlist_tag']:
|
||||||
build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
|
build_log.append(" %s: %s" % (key, repr(opts_dict[key])))
|
||||||
|
@ -1277,7 +1277,6 @@ class CatalogBuilder(object):
|
|||||||
self.opts.log.info('%s' % _format_tag_list(genre_tags_dict, header="enabled genres"))
|
self.opts.log.info('%s' % _format_tag_list(genre_tags_dict, header="enabled genres"))
|
||||||
self.opts.log.info('%s' % _format_tag_list(excluded_tags, header="excluded genres"))
|
self.opts.log.info('%s' % _format_tag_list(excluded_tags, header="excluded genres"))
|
||||||
|
|
||||||
print("genre_tags_dict: %s" % genre_tags_dict)
|
|
||||||
return genre_tags_dict
|
return genre_tags_dict
|
||||||
|
|
||||||
def filter_excluded_genres(self, tags, regex):
|
def filter_excluded_genres(self, tags, regex):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user