Merge branch 'mizaki-talker_settings_generator' into develop
This commit is contained in:
commit
fca5818874
@ -46,6 +46,43 @@ def identifier(parser: settngs.Manager) -> None:
|
||||
action=AppendAction,
|
||||
help="When enabled filters the listed publishers from all search results",
|
||||
)
|
||||
parser.add_setting("--series-match-search-thresh", default=90, type=int)
|
||||
parser.add_setting(
|
||||
"--clear-metadata",
|
||||
default=True,
|
||||
help="Clears all existing metadata during import, default is to merges metadata.\nMay be used in conjunction with -o, -f and -m.\n\n",
|
||||
dest="clear_metadata_on_import",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
)
|
||||
parser.add_setting(
|
||||
"-a",
|
||||
"--auto-imprint",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=False,
|
||||
help="Enables the auto imprint functionality.\ne.g. if the publisher is set to 'vertigo' it\nwill be updated to 'DC Comics' and the imprint\nproperty will be set to 'Vertigo'.\n\n",
|
||||
)
|
||||
|
||||
parser.add_setting(
|
||||
"--sort-series-by-year", default=True, action=argparse.BooleanOptionalAction, help="Sorts series by year"
|
||||
)
|
||||
parser.add_setting(
|
||||
"--exact-series-matches-first",
|
||||
default=True,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Puts series that are an exact match at the top of the list",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--always-use-publisher-filter",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Enables the publisher filter",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--clear-form-before-populating",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Clears all existing metadata when applying metadata from comic source",
|
||||
)
|
||||
|
||||
|
||||
def dialog(parser: settngs.Manager) -> None:
|
||||
@ -86,43 +123,6 @@ def filename(parser: settngs.Manager) -> None:
|
||||
def talker(parser: settngs.Manager) -> None:
|
||||
# General settings for talkers
|
||||
parser.add_setting("--source", default="comicvine", help="Use a specified source by source ID")
|
||||
parser.add_setting("--series-match-search-thresh", default=90, type=int)
|
||||
parser.add_setting(
|
||||
"--clear-metadata",
|
||||
default=True,
|
||||
help="Clears all existing metadata during import, default is to merges metadata.\nMay be used in conjunction with -o, -f and -m.\n\n",
|
||||
dest="clear_metadata_on_import",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
)
|
||||
parser.add_setting(
|
||||
"-a",
|
||||
"--auto-imprint",
|
||||
action=argparse.BooleanOptionalAction,
|
||||
default=False,
|
||||
help="Enables the auto imprint functionality.\ne.g. if the publisher is set to 'vertigo' it\nwill be updated to 'DC Comics' and the imprint\nproperty will be set to 'Vertigo'.\n\n",
|
||||
)
|
||||
|
||||
parser.add_setting(
|
||||
"--sort-series-by-year", default=True, action=argparse.BooleanOptionalAction, help="Sorts series by year"
|
||||
)
|
||||
parser.add_setting(
|
||||
"--exact-series-matches-first",
|
||||
default=True,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Puts series that are an exact match at the top of the list",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--always-use-publisher-filter",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Enables the publisher filter",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--clear-form-before-populating",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Clears all existing metadata when applying metadata from comic source",
|
||||
)
|
||||
|
||||
|
||||
def cbl(parser: settngs.Manager) -> None:
|
||||
|
@ -39,7 +39,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
comic_archive_list: list[ComicArchive],
|
||||
data_style: int,
|
||||
config: settngs.Config[settngs.Namespace],
|
||||
talker: ComicTalker,
|
||||
talkers: dict[str, ComicTalker],
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
@ -55,7 +55,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
)
|
||||
|
||||
self.config = config
|
||||
self.talker = talker
|
||||
self.talkers = talkers
|
||||
self.comic_archive_list = comic_archive_list
|
||||
self.data_style = data_style
|
||||
self.rename_list: list[str] = []
|
||||
@ -160,7 +160,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
self.twList.setSortingEnabled(True)
|
||||
|
||||
def modify_settings(self) -> None:
|
||||
settingswin = SettingsWindow(self, self.config, self.talker)
|
||||
settingswin = SettingsWindow(self, self.config, self.talkers)
|
||||
settingswin.setModal(True)
|
||||
settingswin.show_rename_tab()
|
||||
settingswin.exec()
|
||||
|
@ -25,6 +25,7 @@ from typing import Any
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
import comictaggerlib.ui.talkeruigenerator
|
||||
from comicapi import utils
|
||||
from comicapi.genericmetadata import md_test
|
||||
from comictaggerlib.ctversion import version
|
||||
@ -131,7 +132,7 @@ Spider-Geddon #1 - New Players; Check In
|
||||
|
||||
class SettingsWindow(QtWidgets.QDialog):
|
||||
def __init__(
|
||||
self, parent: QtWidgets.QWidget, config: settngs.Config[settngs.Namespace], talker: ComicTalker
|
||||
self, parent: QtWidgets.QWidget, config: settngs.Config[settngs.Namespace], talkers: dict[str, ComicTalker]
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
@ -142,7 +143,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
)
|
||||
|
||||
self.config = config
|
||||
self.talker = talker
|
||||
self.talkers = talkers
|
||||
self.name = "Settings"
|
||||
|
||||
if platform.system() == "Windows":
|
||||
@ -185,16 +186,21 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.leRenameTemplate.setToolTip(f"<pre>{html.escape(template_tooltip)}</pre>")
|
||||
self.rename_error: Exception | None = None
|
||||
|
||||
self.sources: dict = comictaggerlib.ui.talkeruigenerator.generate_source_option_tabs(
|
||||
self.tComicTalkers, self.config, self.talkers
|
||||
)
|
||||
self.connect_signals()
|
||||
self.settings_to_form()
|
||||
self.rename_test()
|
||||
self.dir_test()
|
||||
|
||||
# Set General as start tab
|
||||
self.tabWidget.setCurrentIndex(0)
|
||||
|
||||
def connect_signals(self) -> None:
|
||||
self.btnBrowseRar.clicked.connect(self.select_rar)
|
||||
self.btnClearCache.clicked.connect(self.clear_cache)
|
||||
self.btnResetSettings.clicked.connect(self.reset_settings)
|
||||
self.btnTestKey.clicked.connect(self.test_api_key)
|
||||
self.btnTemplateHelp.clicked.connect(self.show_template_help)
|
||||
self.cbxMoveFiles.clicked.connect(self.dir_test)
|
||||
self.leDirectory.textEdited.connect(self.dir_test)
|
||||
@ -223,7 +229,6 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.btnRemoveValueReplacement.clicked.disconnect()
|
||||
self.btnResetSettings.clicked.disconnect()
|
||||
self.btnTemplateHelp.clicked.disconnect()
|
||||
self.btnTestKey.clicked.disconnect()
|
||||
self.cbxChangeExtension.clicked.disconnect()
|
||||
self.cbxComplicatedParser.clicked.disconnect()
|
||||
self.cbxMoveFiles.clicked.disconnect()
|
||||
@ -301,7 +306,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
else:
|
||||
self.leRarExePath.setEnabled(False)
|
||||
self.sbNameMatchIdentifyThresh.setValue(self.config[0].identifier_series_match_identify_thresh)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config[0].talker_series_match_search_thresh)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config[0].identifier_series_match_search_thresh)
|
||||
self.tePublisherFilter.setPlainText("\n".join(self.config[0].identifier_publisher_filter))
|
||||
|
||||
self.cbxCheckForNewVersion.setChecked(self.config[0].general_check_for_new_version)
|
||||
@ -312,16 +317,10 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.cbxRemovePublisher.setChecked(self.config[0].filename_remove_publisher)
|
||||
self.switch_parser()
|
||||
|
||||
self.cbxUseSeriesStartAsVolume.setChecked(self.config[0].talker_comicvine_cv_use_series_start_as_volume)
|
||||
self.cbxClearFormBeforePopulating.setChecked(self.config[0].talker_clear_form_before_populating)
|
||||
self.cbxRemoveHtmlTables.setChecked(self.config[0].talker_comicvine_cv_remove_html_tables)
|
||||
|
||||
self.cbxUseFilter.setChecked(self.config[0].talker_always_use_publisher_filter)
|
||||
self.cbxSortByYear.setChecked(self.config[0].talker_sort_series_by_year)
|
||||
self.cbxExactMatches.setChecked(self.config[0].talker_exact_series_matches_first)
|
||||
|
||||
self.leKey.setText(self.config[0].talker_comicvine_comicvine_key)
|
||||
self.leURL.setText(self.config[0].talker_comicvine_comicvine_url)
|
||||
self.cbxClearFormBeforePopulating.setChecked(self.config[0].identifier_clear_form_before_populating)
|
||||
self.cbxUseFilter.setChecked(self.config[0].identifier_always_use_publisher_filter)
|
||||
self.cbxSortByYear.setChecked(self.config[0].identifier_sort_series_by_year)
|
||||
self.cbxExactMatches.setChecked(self.config[0].identifier_exact_series_matches_first)
|
||||
|
||||
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.config[0].cbl_assume_lone_credit_is_primary)
|
||||
self.cbxCopyCharactersToTags.setChecked(self.config[0].cbl_copy_characters_to_tags)
|
||||
@ -349,6 +348,10 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
table.removeRow(i)
|
||||
for row, replacement in enumerate(replacments):
|
||||
self.insertRow(table, row, replacement)
|
||||
|
||||
# Set talker values
|
||||
comictaggerlib.ui.talkeruigenerator.settings_to_talker_form(self.sources, self.config)
|
||||
|
||||
self.connect_signals()
|
||||
|
||||
def get_replacements(self) -> Replacements:
|
||||
@ -418,7 +421,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.config[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked()
|
||||
|
||||
self.config[0].identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
|
||||
self.config[0].talker_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
|
||||
self.config[0].identifier_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
|
||||
self.config[0].identifier_publisher_filter = [
|
||||
x.strip() for x in str(self.tePublisherFilter.toPlainText()).splitlines() if x.strip()
|
||||
]
|
||||
@ -428,16 +431,10 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.config[0].filename_remove_fcbd = self.cbxRemoveFCBD.isChecked()
|
||||
self.config[0].filename_remove_publisher = self.cbxRemovePublisher.isChecked()
|
||||
|
||||
self.config[0].talker_comicvine_cv_use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
|
||||
self.config[0].talker_clear_form_before_populating = self.cbxClearFormBeforePopulating.isChecked()
|
||||
self.config[0].talker_comicvine_cv_remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
|
||||
|
||||
self.config[0].talker_always_use_publisher_filter = self.cbxUseFilter.isChecked()
|
||||
self.config[0].talker_sort_series_by_year = self.cbxSortByYear.isChecked()
|
||||
self.config[0].talker_exact_series_matches_first = self.cbxExactMatches.isChecked()
|
||||
|
||||
self.config[0].talker_comicvine_comicvine_key = self.leKey.text().strip()
|
||||
self.config[0].talker_comicvine_comicvine_url = self.leURL.text().strip()
|
||||
self.config[0].identifier_clear_form_before_populating = self.cbxClearFormBeforePopulating.isChecked()
|
||||
self.config[0].identifier_always_use_publisher_filter = self.cbxUseFilter.isChecked()
|
||||
self.config[0].identifier_sort_series_by_year = self.cbxSortByYear.isChecked()
|
||||
self.config[0].identifier_exact_series_matches_first = self.cbxExactMatches.isChecked()
|
||||
|
||||
self.config[0].cbl_assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()
|
||||
self.config[0].cbl_copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
|
||||
@ -459,6 +456,9 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.config[0].rename_strict = self.cbxRenameStrict.isChecked()
|
||||
self.config[0].rename_replacements = self.get_replacements()
|
||||
|
||||
# Read settings from talker tabs
|
||||
comictaggerlib.ui.talkeruigenerator.form_settings_to_config(self.sources, self.config)
|
||||
|
||||
self.update_talkers_config()
|
||||
|
||||
settngs.save_file(self.config, self.config[0].runtime_config.user_config_dir / "settings.json")
|
||||
@ -467,8 +467,8 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def update_talkers_config(self) -> None:
|
||||
cfg = settngs.normalize_config(self.config, True, True)
|
||||
if f"talker_{self.talker.id}" in cfg[0]:
|
||||
self.talker.parse_settings(cfg[0][f"talker_{self.talker.id}"])
|
||||
for talker, talker_obj in self.talkers.items():
|
||||
talker_obj.parse_settings(cfg[0][f"talker_{talker}"])
|
||||
|
||||
def select_rar(self) -> None:
|
||||
self.select_file(self.leRarExePath, "RAR")
|
||||
@ -478,12 +478,6 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
ComicCacher(self.config[0].runtime_config.user_cache_dir, version).clear_cache()
|
||||
QtWidgets.QMessageBox.information(self, self.name, "Cache has been cleared.")
|
||||
|
||||
def test_api_key(self) -> None:
|
||||
if self.talker.check_api_key(self.leKey.text().strip(), self.leURL.text().strip()):
|
||||
QtWidgets.QMessageBox.information(self, "API Key Test", "Key is valid!")
|
||||
else:
|
||||
QtWidgets.QMessageBox.warning(self, "API Key Test", "Key is NOT valid.")
|
||||
|
||||
def reset_settings(self) -> None:
|
||||
self.config = settngs.get_namespace(settngs.defaults(self.config[1]))
|
||||
self.settings_to_form()
|
||||
|
@ -1354,7 +1354,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
QtWidgets.QMessageBox.warning(self, self.tr("Web Link"), self.tr("Web Link is invalid."))
|
||||
|
||||
def show_settings(self) -> None:
|
||||
settingswin = SettingsWindow(self, self.config, self.current_talker())
|
||||
settingswin = SettingsWindow(self, self.config, self.talkers)
|
||||
settingswin.setModal(True)
|
||||
settingswin.exec()
|
||||
settingswin.result()
|
||||
@ -2047,7 +2047,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if self.dirty_flag_verification(
|
||||
"File Rename", "If you rename files now, unsaved data in the form will be lost. Are you sure?"
|
||||
):
|
||||
dlg = RenameWindow(self, ca_list, self.load_data_style, self.config, self.current_talker())
|
||||
dlg = RenameWindow(self, ca_list, self.load_data_style, self.config, self.talkers)
|
||||
dlg.setModal(True)
|
||||
if dlg.exec() and self.comic_archive is not None:
|
||||
self.fileSelectionList.update_selected_rows()
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>702</width>
|
||||
<height>513</height>
|
||||
<height>559</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -28,7 +28,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tGeneral">
|
||||
<attribute name="title">
|
||||
@ -136,24 +136,14 @@
|
||||
<string>Searching</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>These settings are for the automatic issue identifier which searches online for matches. </p><p>Hover the mouse over an entry field for more info.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="Line" name="line_2">
|
||||
<item row="3" column="0">
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="6" column="0">
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
@ -252,6 +242,44 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>These settings are for the automatic issue identifier which searches online for matches. </p><p>Hover the mouse over an entry field for more info.</p></body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="cbxExactMatches">
|
||||
<property name="text">
|
||||
<string>Initially show Series Name exact matches first</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="cbxSortByYear">
|
||||
<property name="text">
|
||||
<string>Initially sort Series search results by Starting Year instead of No. Issues</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="cbxClearFormBeforePopulating">
|
||||
<property name="text">
|
||||
<string>Clear Form Before Importing Comic Vine data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tFilenameParser">
|
||||
@ -308,197 +336,11 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tComicVine">
|
||||
<widget class="QWidget" name="tComicTalkers">
|
||||
<attribute name="title">
|
||||
<string>Comic Vine</string>
|
||||
<string>Metadata Sources</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxCVTop">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxUseSeriesStartAsVolume">
|
||||
<property name="text">
|
||||
<string>Use Series Start Date as Volume</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxClearFormBeforePopulating">
|
||||
<property name="text">
|
||||
<string>Clear Form Before Importing Comic Vine data</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxRemoveHtmlTables">
|
||||
<property name="text">
|
||||
<string>Remove HTML tables from CV summary field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxSortByYear">
|
||||
<property name="text">
|
||||
<string>Initially sort Series search results by Starting Year instead of No. Issues</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbxExactMatches">
|
||||
<property name="text">
|
||||
<string>Initially show Series Name exact matches first</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="grpBoxKey">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QLabel" name="lblKeyHelp">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><html><head/><body><p>A personal API key from <a href="http://www.comicvine.com/api/"><span style=" text-decoration: underline; color:#0000ff;">Comic Vine</span></a> is recommended in order to search for tag data. Login (or create a new account) there to get your key, and enter it below.</p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QPushButton" name="btnTestKey">
|
||||
<property name="text">
|
||||
<string>Test Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leKey">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="lblKey">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Comic Vine API Key</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="lblURL">
|
||||
<property name="text">
|
||||
<string>Comic Vine URL</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="leURL"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4"/>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tCBL">
|
||||
<attribute name="title">
|
||||
@ -774,25 +616,16 @@
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Replacement</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Strict Only</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
@ -819,25 +652,16 @@
|
||||
<property name="text">
|
||||
<string>Find</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Replacement</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Strict Only</string>
|
||||
</property>
|
||||
<property name="textAlignment">
|
||||
<set>AlignCenter</set>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
|
238
comictaggerlib/ui/talkeruigenerator.py
Normal file
238
comictaggerlib/ui/talkeruigenerator.py
Normal file
@ -0,0 +1,238 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
from functools import partial
|
||||
from typing import NamedTuple
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtWidgets
|
||||
|
||||
from comictalker.comictalker import ComicTalker
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TalkerTab(NamedTuple):
|
||||
tab: QtWidgets.QWidget
|
||||
widgets: dict[str, QtWidgets.QWidget]
|
||||
|
||||
|
||||
def generate_api_widgets(
|
||||
talker_id: str,
|
||||
sources: dict[str, QtWidgets.QWidget],
|
||||
config: settngs.Config[settngs.Namespace],
|
||||
layout: QtWidgets.QGridLayout,
|
||||
talkers: dict[str, ComicTalker],
|
||||
):
|
||||
# *args enforces keyword arguments and allows position arguments to be ignored
|
||||
def call_check_api(*args, le_url: QtWidgets.QLineEdit, le_key: QtWidgets.QLineEdit, talker_id: str):
|
||||
url = ""
|
||||
key = ""
|
||||
if le_key is not None:
|
||||
key = le_key.text().strip()
|
||||
if le_url is not None:
|
||||
url = le_url.text().strip()
|
||||
|
||||
check_text, check_bool = talkers[talker_id].check_api_key(url, key)
|
||||
if check_bool:
|
||||
QtWidgets.QMessageBox.information(None, "API Test Success", check_text)
|
||||
else:
|
||||
QtWidgets.QMessageBox.warning(None, "API Test Failed", check_text)
|
||||
|
||||
# get the actual config objects in case they have overwritten the default
|
||||
talker_key = config[1][f"talker_{talker_id}"][1][f"{talker_id}_key"]
|
||||
talker_url = config[1][f"talker_{talker_id}"][1][f"{talker_id}_url"]
|
||||
btn_test_row = None
|
||||
le_key = None
|
||||
le_url = None
|
||||
|
||||
# only file settings are saved
|
||||
if talker_key.file:
|
||||
# record the current row so we know where to add the button
|
||||
btn_test_row = layout.rowCount()
|
||||
le_key = generate_textbox(talker_key, layout)
|
||||
# To enable setting and getting
|
||||
sources["tabs"][talker_id].widgets[f"talker_{talker_id}_{talker_id}_key"] = le_key
|
||||
|
||||
# only file settings are saved
|
||||
if talker_url.file:
|
||||
# record the current row so we know where to add the button
|
||||
# We overwrite so that the default will be next to the url text box
|
||||
btn_test_row = layout.rowCount()
|
||||
le_url = generate_textbox(talker_url, layout)
|
||||
# To enable setting and getting
|
||||
sources["tabs"][talker_id].widgets[f"talker_{talker_id}_{talker_id}_url"] = le_url
|
||||
|
||||
# The button row was recorded so we add it
|
||||
if btn_test_row is not None:
|
||||
btn = QtWidgets.QPushButton("Test API")
|
||||
layout.addWidget(btn, btn_test_row, 2)
|
||||
# partial is used as connect will pass in event information
|
||||
btn.clicked.connect(partial(call_check_api, le_url=le_url, le_key=le_key, talker_id=talker_id))
|
||||
|
||||
|
||||
def generate_checkbox(option: settngs.Setting, layout: QtWidgets.QGridLayout) -> QtWidgets.QCheckBox:
|
||||
widget = QtWidgets.QCheckBox(option.display_name)
|
||||
widget.setToolTip(option.help)
|
||||
layout.addWidget(widget, layout.rowCount(), 0, 1, -1)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
def generate_spinbox(option: settngs.Setting, layout: QtWidgets.QGridLayout) -> QtWidgets.QSpinBox:
|
||||
row = layout.rowCount()
|
||||
lbl = QtWidgets.QLabel(option.display_name)
|
||||
lbl.setToolTip(option.help)
|
||||
layout.addWidget(lbl, row, 0)
|
||||
widget = QtWidgets.QSpinBox()
|
||||
widget.setRange(0, 9999)
|
||||
widget.setToolTip(option.help)
|
||||
layout.addWidget(widget, row, 1, alignment=QtCore.Qt.AlignLeft)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
def generate_doublespinbox(option: settngs.Setting, layout: QtWidgets.QGridLayout) -> QtWidgets.QDoubleSpinBox:
|
||||
row = layout.rowCount()
|
||||
lbl = QtWidgets.QLabel(option.display_name)
|
||||
lbl.setToolTip(option.help)
|
||||
layout.addWidget(lbl, row, 0)
|
||||
widget = QtWidgets.QDoubleSpinBox()
|
||||
widget.setRange(0, 9999.99)
|
||||
widget.setToolTip(option.help)
|
||||
layout.addWidget(widget, row, 1, alignment=QtCore.Qt.AlignLeft)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
def generate_textbox(option: settngs.Setting, layout: QtWidgets.QGridLayout) -> QtWidgets.QLineEdit:
|
||||
row = layout.rowCount()
|
||||
lbl = QtWidgets.QLabel(option.display_name)
|
||||
lbl.setToolTip(option.help)
|
||||
layout.addWidget(lbl, row, 0)
|
||||
widget = QtWidgets.QLineEdit()
|
||||
widget.setToolTip(option.help)
|
||||
layout.addWidget(widget, row, 1)
|
||||
|
||||
return widget
|
||||
|
||||
|
||||
def settings_to_talker_form(sources: dict[str, QtWidgets.QWidget], config: settngs.Config[settngs.Namespace]) -> None:
|
||||
# Set the active talker via id in sources combo box
|
||||
sources["cbx_select_talker"].setCurrentIndex(sources["cbx_select_talker"].findData(config[0].talker_source))
|
||||
|
||||
for talker in sources["tabs"].items():
|
||||
for name, widget in talker[1].widgets.items():
|
||||
value = getattr(config[0], name)
|
||||
value_type = type(value)
|
||||
try:
|
||||
if value_type is str:
|
||||
widget.setText(value)
|
||||
if value_type is int or value_type is float:
|
||||
widget.setValue(value)
|
||||
if value_type is bool:
|
||||
widget.setChecked(value)
|
||||
except Exception:
|
||||
logger.debug("Failed to set value of %s", name)
|
||||
|
||||
|
||||
def form_settings_to_config(sources: dict[str, QtWidgets.QWidget], config: settngs.Config[settngs.Namespace]) -> None:
|
||||
# Source combo box value
|
||||
config[0].talker_source = sources["cbx_select_talker"].currentData()
|
||||
|
||||
for tab in sources["tabs"].items():
|
||||
for name, widget in tab[1].widgets.items():
|
||||
widget_value = None
|
||||
if isinstance(widget, (QtWidgets.QSpinBox, QtWidgets.QDoubleSpinBox)):
|
||||
widget_value = widget.value()
|
||||
elif isinstance(widget, QtWidgets.QLineEdit):
|
||||
widget_value = widget.text().strip()
|
||||
elif isinstance(widget, QtWidgets.QCheckBox):
|
||||
widget_value = widget.isChecked()
|
||||
|
||||
setattr(config[0], name, widget_value)
|
||||
|
||||
|
||||
def generate_source_option_tabs(
|
||||
comic_talker_tab: QtWidgets.QWidget,
|
||||
config: settngs.Config[settngs.Namespace],
|
||||
talkers: dict[str, ComicTalker],
|
||||
) -> dict[str, QtWidgets.QWidget]:
|
||||
"""
|
||||
Generate GUI tabs and settings for talkers
|
||||
"""
|
||||
|
||||
# Store all widgets as to allow easier access to their values vs. using findChildren etc. on the tab widget
|
||||
sources: dict = {"tabs": {}}
|
||||
|
||||
# Tab comes with a QVBoxLayout
|
||||
comic_talker_tab_layout = comic_talker_tab.layout()
|
||||
|
||||
talker_layout = QtWidgets.QGridLayout()
|
||||
lbl_select_talker = QtWidgets.QLabel("Metadata Source:")
|
||||
cbx_select_talker = QtWidgets.QComboBox()
|
||||
line = QtWidgets.QFrame()
|
||||
line.setFrameShape(QtWidgets.QFrame.HLine)
|
||||
line.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
talker_tabs = QtWidgets.QTabWidget()
|
||||
|
||||
talker_layout.addWidget(lbl_select_talker, 0, 0, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
|
||||
talker_layout.addWidget(cbx_select_talker, 0, 1, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
|
||||
talker_layout.addWidget(line, 1, 0, 1, -1)
|
||||
talker_layout.addWidget(talker_tabs, 2, 0, 1, -1)
|
||||
|
||||
comic_talker_tab_layout.addLayout(talker_layout)
|
||||
|
||||
# Add combobox to sources for getting and setting talker
|
||||
sources["cbx_select_talker"] = cbx_select_talker
|
||||
|
||||
# Add source sub tabs to Comic Sources tab
|
||||
for talker_id, talker_obj in talkers.items():
|
||||
# Add source to general tab dropdown list
|
||||
cbx_select_talker.addItem(talker_obj.name, talker_id)
|
||||
|
||||
tab_name = talker_id
|
||||
sources["tabs"][tab_name] = TalkerTab(tab=QtWidgets.QWidget(), widgets={})
|
||||
layout_grid = QtWidgets.QGridLayout()
|
||||
|
||||
for option in config[1][f"talker_{talker_id}"][1].values():
|
||||
if not option.file:
|
||||
continue
|
||||
if option.dest in (f"{talker_id}_url", f"{talker_id}_key"):
|
||||
continue
|
||||
current_widget = None
|
||||
if option.action is not None and (
|
||||
option.action is argparse.BooleanOptionalAction
|
||||
or option.type is bool
|
||||
or option.action == "store_true"
|
||||
or option.action == "store_false"
|
||||
):
|
||||
current_widget = generate_checkbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
elif option.type is int:
|
||||
current_widget = generate_spinbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
elif option.type is float:
|
||||
current_widget = generate_doublespinbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
# option.type of None should be string
|
||||
elif (option.type is None and option.action is None) or option.type is str:
|
||||
current_widget = generate_textbox(option, layout_grid)
|
||||
sources["tabs"][tab_name]("widget")[option.internal_name] = current_widget
|
||||
else:
|
||||
logger.debug(f"Unsupported talker option found. Name: {option.internal_name} Type: {option.type}")
|
||||
|
||||
# Add talker URL and API key fields
|
||||
generate_api_widgets(talker_id, sources, config, layout_grid, talkers)
|
||||
|
||||
# Add vertical spacer
|
||||
vspacer = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
|
||||
layout_grid.addItem(vspacer, layout_grid.rowCount() + 1, 0)
|
||||
# Display the new widgets
|
||||
sources["tabs"][tab_name].tab.setLayout(layout_grid)
|
||||
|
||||
# Add new sub tab to Comic Source tab
|
||||
talker_tabs.addTab(sources["tabs"][tab_name].tab, talker_obj.name)
|
||||
|
||||
return sources
|
@ -119,7 +119,10 @@ class ComicTalker:
|
||||
self.api_url = self.default_api_url = ""
|
||||
|
||||
def register_settings(self, parser: settngs.Manager) -> None:
|
||||
"""Allows registering settings using the settngs package with an argparse like interface"""
|
||||
"""
|
||||
Allows registering settings using the settngs package with an argparse like interface
|
||||
NOTE: The order used will be reflected within the settings menu
|
||||
"""
|
||||
return None
|
||||
|
||||
def parse_settings(self, settings: dict[str, Any]) -> dict[str, Any]:
|
||||
@ -138,10 +141,15 @@ class ComicTalker:
|
||||
self.api_url = self.default_api_url
|
||||
return settings
|
||||
|
||||
def check_api_key(self, key: str, url: str) -> bool:
|
||||
def check_api_key(self, url: str, key: str) -> tuple[str, bool]:
|
||||
"""
|
||||
This function should return true if the given api key and url are valid.
|
||||
If the Talker does not use an api key it should validate that the url works.
|
||||
This function should return a string with the test outcome for display to user and a bool.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def check_api_url(self, url: str) -> bool:
|
||||
"""
|
||||
This function should return true if the given url is valid.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -173,6 +173,27 @@ class ComicVineTalker(ComicTalker):
|
||||
self.wait_on_ratelimit_time: int = 20
|
||||
|
||||
def register_settings(self, parser: settngs.Manager) -> None:
|
||||
parser.add_setting(
|
||||
"--cv-use-series-start-as-volume",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
display_name="Use series start as volume",
|
||||
help="Use the series start year as the volume number",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--cv-wait-on-ratelimit",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
display_name="Wait on ratelimit",
|
||||
help="Wait when the rate limit is hit",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--cv-remove-html-tables",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
display_name="Remove HTML tables",
|
||||
help="Removes html tables instead of converting them to text",
|
||||
)
|
||||
# The empty string being the default allows this setting to be unset, allowing the default to change
|
||||
parser.add_setting(
|
||||
f"--{self.id}-key",
|
||||
@ -186,14 +207,6 @@ class ComicVineTalker(ComicTalker):
|
||||
display_name="API URL",
|
||||
help=f"Use the given Comic Vine URL. (default: {self.default_api_url})",
|
||||
)
|
||||
parser.add_setting("--cv-use-series-start-as-volume", default=False, action=argparse.BooleanOptionalAction)
|
||||
parser.add_setting("--cv-wait-on-ratelimit", default=False, action=argparse.BooleanOptionalAction)
|
||||
parser.add_setting(
|
||||
"--cv-remove-html-tables",
|
||||
default=False,
|
||||
action=argparse.BooleanOptionalAction,
|
||||
help="Removes html tables instead of converting them to text.",
|
||||
)
|
||||
|
||||
def parse_settings(self, settings: dict[str, Any]) -> dict[str, Any]:
|
||||
settings = super().parse_settings(settings)
|
||||
@ -203,7 +216,7 @@ class ComicVineTalker(ComicTalker):
|
||||
self.remove_html_tables = settings["cv_remove_html_tables"]
|
||||
return settings
|
||||
|
||||
def check_api_key(self, key: str, url: str) -> bool:
|
||||
def check_api_key(self, url: str, key: str) -> tuple[str, bool]:
|
||||
url = talker_utils.fix_url(url)
|
||||
if not url:
|
||||
url = self.default_api_url
|
||||
@ -217,9 +230,12 @@ class ComicVineTalker(ComicTalker):
|
||||
).json()
|
||||
|
||||
# Bogus request, but if the key is wrong, you get error 100: "Invalid API Key"
|
||||
return cv_response["status_code"] != 100
|
||||
if cv_response["status_code"] != 100:
|
||||
return "The API key is valid", True
|
||||
else:
|
||||
return "The API key is INVALID!", False
|
||||
except Exception:
|
||||
return False
|
||||
return "Failed to connect to the URL!", False
|
||||
|
||||
def search_for_series(
|
||||
self,
|
||||
|
Loading…
Reference in New Issue
Block a user