mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Metadata download: Allow specifying rules to transform publisher names in addition to author and tag names. Fixes #2012304 [[Enhancement] in "Metadata Download," add "rules to filter/transform publisher"](https://bugs.launchpad.net/calibre/+bug/2012304)
This commit is contained in:
parent
8a2b4ac5da
commit
b906380184
@ -501,7 +501,8 @@ def identify(log, abort, # {{{
|
|||||||
log('We have %d merged results, merging took: %.2f seconds' %
|
log('We have %d merged results, merging took: %.2f seconds' %
|
||||||
(len(results), time.time() - start_time))
|
(len(results), time.time() - start_time))
|
||||||
tm_rules = msprefs['tag_map_rules']
|
tm_rules = msprefs['tag_map_rules']
|
||||||
if tm_rules:
|
pm_rules = msprefs['publisher_map_rules']
|
||||||
|
if tm_rules or pm_rules:
|
||||||
from calibre.ebooks.metadata.tag_mapper import map_tags
|
from calibre.ebooks.metadata.tag_mapper import map_tags
|
||||||
am_rules = msprefs['author_map_rules']
|
am_rules = msprefs['author_map_rules']
|
||||||
if am_rules:
|
if am_rules:
|
||||||
@ -531,6 +532,9 @@ def identify(log, abort, # {{{
|
|||||||
r.tags = r.tags[:max_tags]
|
r.tags = r.tags[:max_tags]
|
||||||
if getattr(r.pubdate, 'year', 2000) <= UNDEFINED_DATE.year:
|
if getattr(r.pubdate, 'year', 2000) <= UNDEFINED_DATE.year:
|
||||||
r.pubdate = None
|
r.pubdate = None
|
||||||
|
if pm_rules and r.publisher:
|
||||||
|
pubs = map_tags([r.publisher], pm_rules)
|
||||||
|
r.publisher = pubs[0] if pubs else ''
|
||||||
|
|
||||||
if msprefs['swap_author_names']:
|
if msprefs['swap_author_names']:
|
||||||
for r in results:
|
for r in results:
|
||||||
|
@ -18,8 +18,9 @@ msprefs.defaults['swap_author_names'] = False
|
|||||||
msprefs.defaults['fewer_tags'] = True
|
msprefs.defaults['fewer_tags'] = True
|
||||||
msprefs.defaults['find_first_edition_date'] = False
|
msprefs.defaults['find_first_edition_date'] = False
|
||||||
msprefs.defaults['append_comments'] = False
|
msprefs.defaults['append_comments'] = False
|
||||||
msprefs.defaults['tag_map_rules'] = []
|
msprefs.defaults['tag_map_rules'] = ()
|
||||||
msprefs.defaults['author_map_rules'] = []
|
msprefs.defaults['author_map_rules'] = ()
|
||||||
|
msprefs.defaults['publisher_map_rules'] = ()
|
||||||
msprefs.defaults['id_link_rules'] = {}
|
msprefs.defaults['id_link_rules'] = {}
|
||||||
msprefs.defaults['keep_dups'] = False
|
msprefs.defaults['keep_dups'] = False
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ class RuleEdit(RuleEditBase):
|
|||||||
('not_matches', _('does not match regex pattern')),
|
('not_matches', _('does not match regex pattern')),
|
||||||
))
|
))
|
||||||
|
|
||||||
MSG = _('Create the rule below, the rule can be used to add or ignore files')
|
MSG = _('Create the rule below, the rule can be used to add or ignore authors')
|
||||||
SUBJECT = _('the author, if the author name')
|
SUBJECT = _('the author, if the author name')
|
||||||
VALUE_ERROR = _('You must provide a value for the author name to match')
|
VALUE_ERROR = _('You must provide a value for the author name to match')
|
||||||
REPLACE_TEXT = _('with the name:')
|
REPLACE_TEXT = _('with the name:')
|
||||||
|
@ -332,10 +332,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.select_default_button.clicked.connect(self.fields_model.select_user_defaults)
|
self.select_default_button.clicked.connect(self.fields_model.select_user_defaults)
|
||||||
self.select_default_button.clicked.connect(self.changed_signal)
|
self.select_default_button.clicked.connect(self.changed_signal)
|
||||||
self.set_as_default_button.clicked.connect(self.fields_model.commit_user_defaults)
|
self.set_as_default_button.clicked.connect(self.fields_model.commit_user_defaults)
|
||||||
self.tag_map_rules = self.author_map_rules = None
|
self.tag_map_rules = self.author_map_rules = self.publisher_map_rules = None
|
||||||
m = QMenu(self)
|
m = QMenu(self)
|
||||||
m.addAction(_('Tags')).triggered.connect(self.change_tag_map_rules)
|
m.addAction(_('Tags')).triggered.connect(self.change_tag_map_rules)
|
||||||
m.addAction(_('Authors')).triggered.connect(self.change_author_map_rules)
|
m.addAction(_('Authors')).triggered.connect(self.change_author_map_rules)
|
||||||
|
m.addAction(_('Publisher')).triggered.connect(self.change_publisher_map_rules)
|
||||||
self.map_rules_button.setMenu(m)
|
self.map_rules_button.setMenu(m)
|
||||||
l = self.page.layout()
|
l = self.page.layout()
|
||||||
l.setStretch(0, 1)
|
l.setStretch(0, 1)
|
||||||
@ -376,16 +377,25 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
from calibre.gui2.tag_mapper import RulesDialog
|
from calibre.gui2.tag_mapper import RulesDialog
|
||||||
d = RulesDialog(self)
|
d = RulesDialog(self)
|
||||||
if msprefs.get('tag_map_rules'):
|
if msprefs.get('tag_map_rules'):
|
||||||
d.rules = msprefs['tag_map_rules']
|
d.rules = list(msprefs['tag_map_rules'])
|
||||||
if d.exec() == QDialog.DialogCode.Accepted:
|
if d.exec() == QDialog.DialogCode.Accepted:
|
||||||
self.tag_map_rules = d.rules
|
self.tag_map_rules = d.rules
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
|
|
||||||
|
def change_publisher_map_rules(self):
|
||||||
|
from calibre.gui2.publisher_mapper import RulesDialog
|
||||||
|
d = RulesDialog(self)
|
||||||
|
if msprefs.get('publisher_map_rules'):
|
||||||
|
d.rules = list(msprefs['publisher_map_rules'])
|
||||||
|
if d.exec() == QDialog.DialogCode.Accepted:
|
||||||
|
self.publisher_map_rules = d.rules
|
||||||
|
self.changed_signal.emit()
|
||||||
|
|
||||||
def change_author_map_rules(self):
|
def change_author_map_rules(self):
|
||||||
from calibre.gui2.author_mapper import RulesDialog
|
from calibre.gui2.author_mapper import RulesDialog
|
||||||
d = RulesDialog(self)
|
d = RulesDialog(self)
|
||||||
if msprefs.get('author_map_rules'):
|
if msprefs.get('author_map_rules'):
|
||||||
d.rules = msprefs['author_map_rules']
|
d.rules = list(msprefs['author_map_rules'])
|
||||||
if d.exec() == QDialog.DialogCode.Accepted:
|
if d.exec() == QDialog.DialogCode.Accepted:
|
||||||
self.author_map_rules = d.rules
|
self.author_map_rules = d.rules
|
||||||
self.changed_signal.emit()
|
self.changed_signal.emit()
|
||||||
@ -395,7 +405,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
self.sources_model.initialize()
|
self.sources_model.initialize()
|
||||||
self.sources_view.resizeColumnsToContents()
|
self.sources_view.resizeColumnsToContents()
|
||||||
self.fields_model.initialize()
|
self.fields_model.initialize()
|
||||||
self.tag_map_rules = self.author_map_rules = None
|
self.tag_map_rules = self.author_map_rules = self.publisher_map_rules = None
|
||||||
|
|
||||||
def restore_defaults(self):
|
def restore_defaults(self):
|
||||||
ConfigWidgetBase.restore_defaults(self)
|
ConfigWidgetBase.restore_defaults(self)
|
||||||
@ -410,6 +420,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
msprefs['tag_map_rules'] = self.tag_map_rules or []
|
msprefs['tag_map_rules'] = self.tag_map_rules or []
|
||||||
if self.author_map_rules is not None:
|
if self.author_map_rules is not None:
|
||||||
msprefs['author_map_rules'] = self.author_map_rules or []
|
msprefs['author_map_rules'] = self.author_map_rules or []
|
||||||
|
if self.publisher_map_rules is not None:
|
||||||
|
msprefs['publisher_map_rules'] = self.publisher_map_rules or []
|
||||||
return ConfigWidgetBase.commit(self)
|
return ConfigWidgetBase.commit(self)
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Create &rules to transform tags/authors</string>
|
<string>Create &rules to transform tags/authors/publishers</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="popupMode">
|
<property name="popupMode">
|
||||||
<enum>QToolButton::InstantPopup</enum>
|
<enum>QToolButton::InstantPopup</enum>
|
||||||
|
137
src/calibre/gui2/publisher_mapper.py
Normal file
137
src/calibre/gui2/publisher_mapper.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from calibre.ebooks.metadata.tag_mapper import map_tags
|
||||||
|
from calibre.gui2 import Application, elided_text
|
||||||
|
from calibre.gui2.tag_mapper import (
|
||||||
|
RuleEdit as RuleEditBase, RuleEditDialog as RuleEditDialogBase,
|
||||||
|
RuleItem as RuleItemBase, Rules as RulesBase, RulesDialog as RulesDialogBase,
|
||||||
|
Tester as TesterBase,
|
||||||
|
)
|
||||||
|
from calibre.utils.config import JSONConfig
|
||||||
|
|
||||||
|
publisher_maps = JSONConfig('publisher-mapping-rules')
|
||||||
|
|
||||||
|
|
||||||
|
class RuleEdit(RuleEditBase):
|
||||||
|
|
||||||
|
ACTION_MAP = OrderedDict((
|
||||||
|
('replace', _('Change')),
|
||||||
|
('capitalize', _('Capitalize')),
|
||||||
|
('titlecase', _('Title-case')),
|
||||||
|
('lower', _('Lower-case')),
|
||||||
|
('upper', _('Upper-case')),
|
||||||
|
))
|
||||||
|
|
||||||
|
MATCH_TYPE_MAP = OrderedDict((
|
||||||
|
('one_of', _('is one of')),
|
||||||
|
('not_one_of', _('is not one of')),
|
||||||
|
('has', _('contains')),
|
||||||
|
('matches', _('matches regex pattern')),
|
||||||
|
('not_matches', _('does not match regex pattern')),
|
||||||
|
))
|
||||||
|
|
||||||
|
MSG = _('Create the rule below, the rule can be used to modify publishers')
|
||||||
|
SUBJECT = _('the publisher, if the publisher name')
|
||||||
|
VALUE_ERROR = _('You must provide a value for the publisher name to match')
|
||||||
|
REPLACE_TEXT = _('with the name:')
|
||||||
|
SINGLE_EDIT_FIELD_NAME = 'publisher'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def can_use_tag_editor(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def update_state(self):
|
||||||
|
a = self.action.currentData()
|
||||||
|
replace = a == 'replace'
|
||||||
|
self.la3.setVisible(replace), self.replace.setVisible(replace)
|
||||||
|
m = self.match_type.currentData()
|
||||||
|
is_match = 'matches' in m
|
||||||
|
self.regex_help.setVisible(is_match)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rule(self):
|
||||||
|
return {
|
||||||
|
'action': self.action.currentData(),
|
||||||
|
'match_type': self.match_type.currentData(),
|
||||||
|
'query': self.query.text().strip(),
|
||||||
|
'replace': self.replace.text().strip(),
|
||||||
|
}
|
||||||
|
|
||||||
|
@rule.setter
|
||||||
|
def rule(self, rule):
|
||||||
|
def sc(name):
|
||||||
|
c = getattr(self, name)
|
||||||
|
idx = c.findData(str(rule.get(name, '')))
|
||||||
|
if idx < 0:
|
||||||
|
idx = 0
|
||||||
|
c.setCurrentIndex(idx)
|
||||||
|
sc('match_type'), sc('action')
|
||||||
|
self.query.setText(str(rule.get('query', '')).strip())
|
||||||
|
self.replace.setText(str(rule.get('replace', '')).strip())
|
||||||
|
|
||||||
|
|
||||||
|
class RuleEditDialog(RuleEditDialogBase):
|
||||||
|
|
||||||
|
PREFS_NAME = 'edit-publisher-mapping-rule'
|
||||||
|
RuleEditClass = RuleEdit
|
||||||
|
|
||||||
|
|
||||||
|
class RuleItem(RuleItemBase):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def text_from_rule(rule, parent):
|
||||||
|
query = elided_text(rule['query'], font=parent.font(), width=200, pos='right')
|
||||||
|
text = _(
|
||||||
|
'<b>{action}</b> the publisher name, if it <i>{match_type}</i>: <b>{query}</b>').format(
|
||||||
|
action=RuleEdit.ACTION_MAP[rule['action']], match_type=RuleEdit.MATCH_TYPE_MAP[rule['match_type']], query=query)
|
||||||
|
if rule['action'] == 'replace':
|
||||||
|
text += '<br>' + _('to the name') + ' <b>%s</b>' % rule['replace']
|
||||||
|
return '<div style="white-space: nowrap">' + text + '</div>'
|
||||||
|
|
||||||
|
|
||||||
|
class Rules(RulesBase):
|
||||||
|
|
||||||
|
RuleItemClass = RuleItem
|
||||||
|
RuleEditDialogClass = RuleEditDialog
|
||||||
|
MSG = _('You can specify rules to manipulate publisher names here.'
|
||||||
|
' Click the "Add Rule" button'
|
||||||
|
' below to get started. The rules will be processed in order for every publisher.')
|
||||||
|
|
||||||
|
|
||||||
|
class Tester(TesterBase):
|
||||||
|
|
||||||
|
DIALOG_TITLE = _('Test publisher mapping rules')
|
||||||
|
PREFS_NAME = 'test-publisher-mapping-rules'
|
||||||
|
LABEL = _('Enter an publisher name to test:')
|
||||||
|
PLACEHOLDER = _('Enter publisher and click the "Test" button')
|
||||||
|
EMPTY_RESULT = '<p> </p>'
|
||||||
|
|
||||||
|
def do_test(self):
|
||||||
|
publisher = self.value.strip()
|
||||||
|
ans = map_tags([publisher], self.rules)
|
||||||
|
self.result.setText((ans or ('',))[0])
|
||||||
|
|
||||||
|
|
||||||
|
class RulesDialog(RulesDialogBase):
|
||||||
|
|
||||||
|
DIALOG_TITLE = _('Edit publisher mapping rules')
|
||||||
|
PREFS_NAME = 'edit-publisher-mapping-rules'
|
||||||
|
RulesClass = Rules
|
||||||
|
TesterClass = Tester
|
||||||
|
PREFS_OBJECT = publisher_maps
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = Application([])
|
||||||
|
d = RulesDialog()
|
||||||
|
d.rules = [
|
||||||
|
{'action':'replace', 'query':'alice Bob', 'match_type':'one_of', 'replace':'Alice Bob'},
|
||||||
|
]
|
||||||
|
d.exec()
|
||||||
|
from pprint import pprint
|
||||||
|
pprint(d.rules)
|
||||||
|
del d, app
|
Loading…
x
Reference in New Issue
Block a user