Update setting group names
Make group names presentable to users and add builtin plugins during namespace generation. Revamp talkeruigenerator.py to use generated group and setting names and remove as much hard-coded strings as possible Add a --list-plugins commandline option
This commit is contained in:
parent
90eb1c3980
commit
05e6eaf88e
@ -9,6 +9,7 @@ import comictaggerlib.main
|
||||
|
||||
def generate() -> str:
|
||||
app = comictaggerlib.main.App()
|
||||
app.load_plugins(app.initial_arg_parser.parse_known_args()[0])
|
||||
app.register_settings()
|
||||
return settngs.generate_ns(app.manager.definitions)
|
||||
|
||||
|
@ -52,7 +52,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
self.current_match_set: MultipleMatch = match_set_list[0]
|
||||
|
||||
self.altCoverWidget = CoverImageWidget(
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode, config.runtime_config.user_cache_dir, talker
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode, config.Runtime_Options_config.user_cache_dir, talker
|
||||
)
|
||||
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
|
||||
gridlayout.addWidget(self.altCoverWidget)
|
||||
@ -233,10 +233,10 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
md = ca.read_metadata(self._style)
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
self.config.filename_complicated_parser,
|
||||
self.config.filename_remove_c2c,
|
||||
self.config.filename_remove_fcbd,
|
||||
self.config.filename_remove_publisher,
|
||||
self.config.Filename_Parsing_complicated_parser,
|
||||
self.config.Filename_Parsing_remove_c2c,
|
||||
self.config.Filename_Parsing_remove_fcbd,
|
||||
self.config.Filename_Parsing_remove_publisher,
|
||||
)
|
||||
|
||||
# now get the particular issue data
|
||||
|
@ -40,15 +40,15 @@ class AutoTagStartWindow(QtWidgets.QDialog):
|
||||
|
||||
self.cbxSpecifySearchString.setChecked(False)
|
||||
self.cbxSplitWords.setChecked(False)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config.identifier_series_match_identify_thresh)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config.Issue_Identifier_series_match_identify_thresh)
|
||||
self.leSearchString.setEnabled(False)
|
||||
|
||||
self.cbxSaveOnLowConfidence.setChecked(self.config.autotag_save_on_low_confidence)
|
||||
self.cbxDontUseYear.setChecked(self.config.autotag_dont_use_year_when_identifying)
|
||||
self.cbxAssumeIssueOne.setChecked(self.config.autotag_assume_1_if_no_issue_num)
|
||||
self.cbxIgnoreLeadingDigitsInFilename.setChecked(self.config.autotag_ignore_leading_numbers_in_filename)
|
||||
self.cbxRemoveAfterSuccess.setChecked(self.config.autotag_remove_archive_after_successful_match)
|
||||
self.cbxAutoImprint.setChecked(self.config.identifier_auto_imprint)
|
||||
self.cbxSaveOnLowConfidence.setChecked(self.config.Auto_Tag_save_on_low_confidence)
|
||||
self.cbxDontUseYear.setChecked(self.config.Auto_Tag_dont_use_year_when_identifying)
|
||||
self.cbxAssumeIssueOne.setChecked(self.config.Auto_Tag_assume_1_if_no_issue_num)
|
||||
self.cbxIgnoreLeadingDigitsInFilename.setChecked(self.config.Auto_Tag_ignore_leading_numbers_in_filename)
|
||||
self.cbxRemoveAfterSuccess.setChecked(self.config.Auto_Tag_remove_archive_after_successful_match)
|
||||
self.cbxAutoImprint.setChecked(self.config.Issue_Identifier_auto_imprint)
|
||||
|
||||
nlmt_tip = """<html>The <b>Name Match Ratio Threshold: Auto-Identify</b> is for eliminating automatic
|
||||
search matches that are too long compared to your series name search. The lower
|
||||
@ -73,7 +73,7 @@ class AutoTagStartWindow(QtWidgets.QDialog):
|
||||
self.ignore_leading_digits_in_filename = False
|
||||
self.remove_after_success = False
|
||||
self.search_string = ""
|
||||
self.name_length_match_tolerance = self.config.identifier_series_match_search_thresh
|
||||
self.name_length_match_tolerance = self.config.Issue_Identifier_series_match_search_thresh
|
||||
self.split_words = self.cbxSplitWords.isChecked()
|
||||
|
||||
def search_string_toggle(self) -> None:
|
||||
@ -92,11 +92,11 @@ class AutoTagStartWindow(QtWidgets.QDialog):
|
||||
self.split_words = self.cbxSplitWords.isChecked()
|
||||
|
||||
# persist some settings
|
||||
self.config.autotag_save_on_low_confidence = self.auto_save_on_low
|
||||
self.config.autotag_dont_use_year_when_identifying = self.dont_use_year
|
||||
self.config.autotag_assume_1_if_no_issue_num = self.assume_issue_one
|
||||
self.config.autotag_ignore_leading_numbers_in_filename = self.ignore_leading_digits_in_filename
|
||||
self.config.autotag_remove_archive_after_successful_match = self.remove_after_success
|
||||
self.config.Auto_Tag_save_on_low_confidence = self.auto_save_on_low
|
||||
self.config.Auto_Tag_dont_use_year_when_identifying = self.dont_use_year
|
||||
self.config.Auto_Tag_assume_1_if_no_issue_num = self.assume_issue_one
|
||||
self.config.Auto_Tag_ignore_leading_numbers_in_filename = self.ignore_leading_digits_in_filename
|
||||
self.config.Auto_Tag_remove_archive_after_successful_match = self.remove_after_success
|
||||
|
||||
if self.cbxSpecifySearchString.isChecked():
|
||||
self.search_string = self.leSearchString.text()
|
||||
|
@ -29,7 +29,7 @@ class CBLTransformer:
|
||||
self.config = config
|
||||
|
||||
def apply(self) -> GenericMetadata:
|
||||
if self.config.cbl_assume_lone_credit_is_primary:
|
||||
if self.config.Comic_Book_Lover_assume_lone_credit_is_primary:
|
||||
# helper
|
||||
def set_lone_primary(role_list: list[str]) -> tuple[Credit | None, int]:
|
||||
lone_credit: Credit | None = None
|
||||
@ -55,19 +55,19 @@ class CBLTransformer:
|
||||
c["primary"] = False
|
||||
self.metadata.add_credit(c["person"], "Artist", True)
|
||||
|
||||
if self.config.cbl_copy_characters_to_tags:
|
||||
if self.config.Comic_Book_Lover_copy_characters_to_tags:
|
||||
self.metadata.tags.update(x for x in self.metadata.characters)
|
||||
|
||||
if self.config.cbl_copy_teams_to_tags:
|
||||
if self.config.Comic_Book_Lover_copy_teams_to_tags:
|
||||
self.metadata.tags.update(x for x in self.metadata.teams)
|
||||
|
||||
if self.config.cbl_copy_locations_to_tags:
|
||||
if self.config.Comic_Book_Lover_copy_locations_to_tags:
|
||||
self.metadata.tags.update(x for x in self.metadata.locations)
|
||||
|
||||
if self.config.cbl_copy_storyarcs_to_tags:
|
||||
if self.config.Comic_Book_Lover_copy_storyarcs_to_tags:
|
||||
self.metadata.tags.update(x for x in self.metadata.story_arcs)
|
||||
|
||||
if self.config.cbl_copy_notes_to_comments:
|
||||
if self.config.Comic_Book_Lover_copy_notes_to_comments:
|
||||
if self.metadata.notes is not None:
|
||||
if self.metadata.description is None:
|
||||
self.metadata.description = ""
|
||||
@ -76,7 +76,7 @@ class CBLTransformer:
|
||||
if self.metadata.notes not in self.metadata.description:
|
||||
self.metadata.description += self.metadata.notes
|
||||
|
||||
if self.config.cbl_copy_weblink_to_comments:
|
||||
if self.config.Comic_Book_Lover_copy_weblink_to_comments:
|
||||
if self.metadata.web_link is not None:
|
||||
if self.metadata.description is None:
|
||||
self.metadata.description = ""
|
||||
|
@ -46,9 +46,9 @@ class CLI:
|
||||
self.batch_mode = False
|
||||
|
||||
def current_talker(self) -> ComicTalker:
|
||||
if self.config.talker_source in self.talkers:
|
||||
return self.talkers[self.config.talker_source]
|
||||
logger.error("Could not find the '%s' talker", self.config.talker_source)
|
||||
if self.config.Sources_source in self.talkers:
|
||||
return self.talkers[self.config.Sources_source]
|
||||
logger.error("Could not find the '%s' talker", self.config.Sources_source)
|
||||
raise SystemExit(2)
|
||||
|
||||
def actual_issue_data_fetch(self, issue_id: str) -> GenericMetadata:
|
||||
@ -59,14 +59,14 @@ class CLI:
|
||||
logger.exception(f"Error retrieving issue details. Save aborted.\n{e}")
|
||||
return GenericMetadata()
|
||||
|
||||
if self.config.cbl_apply_transform_on_import:
|
||||
if self.config.Comic_Book_Lover_apply_transform_on_import:
|
||||
ct_md = CBLTransformer(ct_md, self.config).apply()
|
||||
|
||||
return ct_md
|
||||
|
||||
def actual_metadata_save(self, ca: ComicArchive, md: GenericMetadata) -> bool:
|
||||
if not self.config.runtime_dryrun:
|
||||
for metadata_style in self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_dryrun:
|
||||
for metadata_style in self.config.Runtime_Options_type:
|
||||
# write out the new data
|
||||
if not ca.write_metadata(md, metadata_style):
|
||||
logger.error("The tag save seemed to fail for style: %s!", MetaDataStyle.name[metadata_style])
|
||||
@ -75,7 +75,7 @@ class CLI:
|
||||
print("Save complete.")
|
||||
logger.info("Save complete.")
|
||||
else:
|
||||
if self.config.runtime_quiet:
|
||||
if self.config.Runtime_Options_quiet:
|
||||
logger.info("dry-run option was set, so nothing was written")
|
||||
print("dry-run option was set, so nothing was written")
|
||||
else:
|
||||
@ -102,7 +102,7 @@ class CLI:
|
||||
m["issue_title"],
|
||||
)
|
||||
)
|
||||
if self.config.runtime_interactive:
|
||||
if self.config.Runtime_Options_interactive:
|
||||
while True:
|
||||
i = input("Choose a match #, or 's' to skip: ")
|
||||
if (i.isdigit() and int(i) in range(1, len(match_set.matches) + 1)) or i == "s":
|
||||
@ -113,7 +113,7 @@ class CLI:
|
||||
ca = match_set.ca
|
||||
md = self.create_local_metadata(ca)
|
||||
ct_md = self.actual_issue_data_fetch(match_set.matches[int(i) - 1]["issue_id"])
|
||||
if self.config.identifier_clear_metadata_on_import:
|
||||
if self.config.Issue_Identifier_clear_metadata_on_import:
|
||||
md = ct_md
|
||||
else:
|
||||
notes = (
|
||||
@ -122,14 +122,14 @@ class CLI:
|
||||
)
|
||||
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
|
||||
|
||||
if self.config.identifier_auto_imprint:
|
||||
if self.config.Issue_Identifier_auto_imprint:
|
||||
md.fix_publisher()
|
||||
|
||||
self.actual_metadata_save(ca, md)
|
||||
|
||||
def post_process_matches(self, match_results: OnlineMatchResults) -> None:
|
||||
# now go through the match results
|
||||
if self.config.runtime_summary:
|
||||
if self.config.Runtime_Options_summary:
|
||||
if len(match_results.good_matches) > 0:
|
||||
print("\nSuccessful matches:\n------------------")
|
||||
for f in match_results.good_matches:
|
||||
@ -150,7 +150,7 @@ class CLI:
|
||||
for f in match_results.fetch_data_failures:
|
||||
print(f)
|
||||
|
||||
if not self.config.runtime_summary and not self.config.runtime_interactive:
|
||||
if not self.config.Runtime_Options_summary and not self.config.Runtime_Options_interactive:
|
||||
# just quit if we're not interactive or showing the summary
|
||||
return
|
||||
|
||||
@ -170,14 +170,14 @@ class CLI:
|
||||
self.display_match_set_for_choice(label, match_set)
|
||||
|
||||
def run(self) -> None:
|
||||
if len(self.config.runtime_files) < 1:
|
||||
if len(self.config.Runtime_Options_files) < 1:
|
||||
logger.error("You must specify at least one filename. Use the -h option for more info")
|
||||
return
|
||||
|
||||
match_results = OnlineMatchResults()
|
||||
self.batch_mode = len(self.config.runtime_files) > 1
|
||||
self.batch_mode = len(self.config.Runtime_Options_files) > 1
|
||||
|
||||
for f in self.config.runtime_files:
|
||||
for f in self.config.Runtime_Options_files:
|
||||
self.process_file_cli(f, match_results)
|
||||
sys.stdout.flush()
|
||||
|
||||
@ -190,18 +190,18 @@ class CLI:
|
||||
md.set_default_page_list(ca.get_number_of_pages())
|
||||
|
||||
# now, overlay the parsed filename info
|
||||
if self.config.runtime_parse_filename:
|
||||
if self.config.Runtime_Options_parse_filename:
|
||||
f_md = ca.metadata_from_filename(
|
||||
self.config.filename_complicated_parser,
|
||||
self.config.filename_remove_c2c,
|
||||
self.config.filename_remove_fcbd,
|
||||
self.config.filename_remove_publisher,
|
||||
self.config.runtime_split_words,
|
||||
self.config.Filename_Parsing_complicated_parser,
|
||||
self.config.Filename_Parsing_remove_c2c,
|
||||
self.config.Filename_Parsing_remove_fcbd,
|
||||
self.config.Filename_Parsing_remove_publisher,
|
||||
self.config.Runtime_Options_split_words,
|
||||
)
|
||||
|
||||
md.overlay(f_md)
|
||||
|
||||
for metadata_style in self.config.runtime_type:
|
||||
for metadata_style in self.config.Runtime_Options_type:
|
||||
if ca.has_metadata(metadata_style):
|
||||
try:
|
||||
t_md = ca.read_metadata(metadata_style)
|
||||
@ -211,12 +211,12 @@ class CLI:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
# finally, use explicit stuff
|
||||
md.overlay(self.config.runtime_metadata)
|
||||
md.overlay(self.config.Runtime_Options_metadata)
|
||||
|
||||
return md
|
||||
|
||||
def print(self, ca: ComicArchive) -> None:
|
||||
if not self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_type:
|
||||
page_count = ca.get_number_of_pages()
|
||||
|
||||
brief = ""
|
||||
@ -246,38 +246,38 @@ class CLI:
|
||||
|
||||
print(brief)
|
||||
|
||||
if self.config.runtime_quiet:
|
||||
if self.config.Runtime_Options_quiet:
|
||||
return
|
||||
|
||||
print()
|
||||
|
||||
if not self.config.runtime_type or MetaDataStyle.CIX in self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_type or MetaDataStyle.CIX in self.config.Runtime_Options_type:
|
||||
if ca.has_metadata(MetaDataStyle.CIX):
|
||||
print("--------- ComicRack tags ---------")
|
||||
try:
|
||||
if self.config.runtime_raw:
|
||||
if self.config.Runtime_Options_raw:
|
||||
print(ca.read_raw_cix())
|
||||
else:
|
||||
print(ca.read_cix())
|
||||
except Exception as e:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
if not self.config.runtime_type or MetaDataStyle.CBI in self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_type or MetaDataStyle.CBI in self.config.Runtime_Options_type:
|
||||
if ca.has_metadata(MetaDataStyle.CBI):
|
||||
print("------- ComicBookLover tags -------")
|
||||
try:
|
||||
if self.config.runtime_raw:
|
||||
if self.config.Runtime_Options_raw:
|
||||
pprint(json.loads(ca.read_raw_cbi()))
|
||||
else:
|
||||
print(ca.read_cbi())
|
||||
except Exception as e:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
if not self.config.runtime_type or MetaDataStyle.COMET in self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_type or MetaDataStyle.COMET in self.config.Runtime_Options_type:
|
||||
if ca.has_metadata(MetaDataStyle.COMET):
|
||||
print("----------- CoMet tags -----------")
|
||||
try:
|
||||
if self.config.runtime_raw:
|
||||
if self.config.Runtime_Options_raw:
|
||||
print(ca.read_raw_comet())
|
||||
else:
|
||||
print(ca.read_comet())
|
||||
@ -285,10 +285,10 @@ class CLI:
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
def delete(self, ca: ComicArchive) -> None:
|
||||
for metadata_style in self.config.runtime_type:
|
||||
for metadata_style in self.config.Runtime_Options_type:
|
||||
style_name = MetaDataStyle.name[metadata_style]
|
||||
if ca.has_metadata(metadata_style):
|
||||
if not self.config.runtime_dryrun:
|
||||
if not self.config.Runtime_Options_dryrun:
|
||||
if not ca.remove_metadata(metadata_style):
|
||||
print(f"{ca.path}: Tag removal seemed to fail!")
|
||||
else:
|
||||
@ -299,25 +299,25 @@ class CLI:
|
||||
print(f"{ca.path}: This archive doesn't have {style_name} tags to remove.")
|
||||
|
||||
def copy(self, ca: ComicArchive) -> None:
|
||||
for metadata_style in self.config.runtime_type:
|
||||
for metadata_style in self.config.Runtime_Options_type:
|
||||
dst_style_name = MetaDataStyle.name[metadata_style]
|
||||
if not self.config.runtime_overwrite and ca.has_metadata(metadata_style):
|
||||
if not self.config.Runtime_Options_overwrite and ca.has_metadata(metadata_style):
|
||||
print(f"{ca.path}: Already has {dst_style_name} tags. Not overwriting.")
|
||||
return
|
||||
if self.config.commands_copy == metadata_style:
|
||||
if self.config.Commands_copy == metadata_style:
|
||||
print(f"{ca.path}: Destination and source are same: {dst_style_name}. Nothing to do.")
|
||||
return
|
||||
|
||||
src_style_name = MetaDataStyle.name[self.config.commands_copy]
|
||||
if ca.has_metadata(self.config.commands_copy):
|
||||
if not self.config.runtime_dryrun:
|
||||
src_style_name = MetaDataStyle.name[self.config.Commands_copy]
|
||||
if ca.has_metadata(self.config.Commands_copy):
|
||||
if not self.config.Runtime_Options_dryrun:
|
||||
try:
|
||||
md = ca.read_metadata(self.config.commands_copy)
|
||||
md = ca.read_metadata(self.config.Commands_copy)
|
||||
except Exception as e:
|
||||
md = GenericMetadata()
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
|
||||
if self.config.cbl_apply_transform_on_bulk_operation == MetaDataStyle.CBI:
|
||||
if self.config.Comic_Book_Lover_apply_transform_on_bulk_operation == MetaDataStyle.CBI:
|
||||
md = CBLTransformer(md, self.config).apply()
|
||||
|
||||
if not ca.write_metadata(md, metadata_style):
|
||||
@ -330,8 +330,8 @@ class CLI:
|
||||
print(f"{ca.path}: This archive doesn't have {src_style_name} tags to copy.")
|
||||
|
||||
def save(self, ca: ComicArchive, match_results: OnlineMatchResults) -> None:
|
||||
if not self.config.runtime_overwrite:
|
||||
for metadata_style in self.config.runtime_type:
|
||||
if not self.config.Runtime_Options_overwrite:
|
||||
for metadata_style in self.config.Runtime_Options_type:
|
||||
if ca.has_metadata(metadata_style):
|
||||
print(f"{ca.path}: Already has {MetaDataStyle.name[metadata_style]} tags. Not overwriting.")
|
||||
return
|
||||
@ -341,26 +341,26 @@ class CLI:
|
||||
|
||||
md = self.create_local_metadata(ca)
|
||||
if md.issue is None or md.issue == "":
|
||||
if self.config.autotag_assume_1_if_no_issue_num:
|
||||
if self.config.Auto_Tag_assume_1_if_no_issue_num:
|
||||
md.issue = "1"
|
||||
|
||||
# now, search online
|
||||
if self.config.runtime_online:
|
||||
if self.config.runtime_issue_id is not None:
|
||||
if self.config.Runtime_Options_online:
|
||||
if self.config.Runtime_Options_issue_id is not None:
|
||||
# we were given the actual issue ID to search with
|
||||
try:
|
||||
ct_md = self.current_talker().fetch_comic_data(self.config.runtime_issue_id)
|
||||
ct_md = self.current_talker().fetch_comic_data(self.config.Runtime_Options_issue_id)
|
||||
except TalkerError as e:
|
||||
logger.exception(f"Error retrieving issue details. Save aborted.\n{e}")
|
||||
match_results.fetch_data_failures.append(str(ca.path.absolute()))
|
||||
return
|
||||
|
||||
if ct_md is None:
|
||||
logger.error("No match for ID %s was found.", self.config.runtime_issue_id)
|
||||
logger.error("No match for ID %s was found.", self.config.Runtime_Options_issue_id)
|
||||
match_results.no_matches.append(str(ca.path.absolute()))
|
||||
return
|
||||
|
||||
if self.config.cbl_apply_transform_on_import:
|
||||
if self.config.Comic_Book_Lover_apply_transform_on_import:
|
||||
ct_md = CBLTransformer(ct_md, self.config).apply()
|
||||
else:
|
||||
if md is None or md.is_empty:
|
||||
@ -371,7 +371,7 @@ class CLI:
|
||||
ii = IssueIdentifier(ca, self.config, self.current_talker())
|
||||
|
||||
def myoutput(text: str) -> None:
|
||||
if self.config.runtime_verbose:
|
||||
if self.config.Runtime_Options_verbose:
|
||||
IssueIdentifier.default_write_output(text)
|
||||
|
||||
# use our overlaid MD struct to search
|
||||
@ -411,7 +411,7 @@ class CLI:
|
||||
logger.error("Online search: Multiple good matches. Save aborted")
|
||||
match_results.multiple_matches.append(MultipleMatch(ca, matches))
|
||||
return
|
||||
if low_confidence and self.config.runtime_abort_on_low_confidence:
|
||||
if low_confidence and self.config.Runtime_Options_abort_on_low_confidence:
|
||||
logger.error("Online search: Low confidence match. Save aborted")
|
||||
match_results.low_confidence_matches.append(MultipleMatch(ca, matches))
|
||||
return
|
||||
@ -428,7 +428,7 @@ class CLI:
|
||||
match_results.fetch_data_failures.append(str(ca.path.absolute()))
|
||||
return
|
||||
|
||||
if self.config.identifier_clear_metadata_on_import:
|
||||
if self.config.Issue_Identifier_clear_metadata_on_import:
|
||||
md = GenericMetadata()
|
||||
|
||||
notes = (
|
||||
@ -438,11 +438,11 @@ class CLI:
|
||||
md.overlay(
|
||||
ct_md.replace(
|
||||
notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger"),
|
||||
description=cleanup_html(ct_md.description, self.config.talker_remove_html_tables),
|
||||
description=cleanup_html(ct_md.description, self.config.Sources_remove_html_tables),
|
||||
)
|
||||
)
|
||||
|
||||
if self.config.identifier_auto_imprint:
|
||||
if self.config.Issue_Identifier_auto_imprint:
|
||||
md.fix_publisher()
|
||||
|
||||
# ok, done building our metadata. time to save
|
||||
@ -464,18 +464,18 @@ class CLI:
|
||||
return
|
||||
|
||||
new_ext = "" # default
|
||||
if self.config.rename_set_extension_based_on_archive:
|
||||
if self.config.File_Rename_set_extension_based_on_archive:
|
||||
new_ext = ca.extension()
|
||||
|
||||
renamer = FileRenamer(
|
||||
md,
|
||||
platform="universal" if self.config.rename_strict else "auto",
|
||||
replacements=self.config.rename_replacements,
|
||||
platform="universal" if self.config.File_Rename_strict else "auto",
|
||||
replacements=self.config.File_Rename_replacements,
|
||||
)
|
||||
renamer.set_template(self.config.rename_template)
|
||||
renamer.set_issue_zero_padding(self.config.rename_issue_number_padding)
|
||||
renamer.set_smart_cleanup(self.config.rename_use_smart_string_cleanup)
|
||||
renamer.move = self.config.rename_move_to_dir
|
||||
renamer.set_template(self.config.File_Rename_template)
|
||||
renamer.set_issue_zero_padding(self.config.File_Rename_issue_number_padding)
|
||||
renamer.set_smart_cleanup(self.config.File_Rename_use_smart_string_cleanup)
|
||||
renamer.move = self.config.File_Rename_move_to_dir
|
||||
|
||||
try:
|
||||
new_name = renamer.determine_name(ext=new_ext)
|
||||
@ -487,14 +487,14 @@ class CLI:
|
||||
"Please consult the template help in the settings "
|
||||
"and the documentation on the format at "
|
||||
"https://docs.python.org/3/library/string.html#format-string-syntax",
|
||||
self.config.rename_template,
|
||||
self.config.File_Rename_template,
|
||||
)
|
||||
return
|
||||
except Exception:
|
||||
logger.exception("Formatter failure: %s metadata: %s", self.config.rename_template, renamer.metadata)
|
||||
logger.exception("Formatter failure: %s metadata: %s", self.config.File_Rename_template, renamer.metadata)
|
||||
return
|
||||
|
||||
folder = get_rename_dir(ca, self.config.rename_dir if self.config.rename_move_to_dir else None)
|
||||
folder = get_rename_dir(ca, self.config.File_Rename_dir if self.config.File_Rename_move_to_dir else None)
|
||||
|
||||
full_path = folder / new_name
|
||||
|
||||
@ -503,7 +503,7 @@ class CLI:
|
||||
return
|
||||
|
||||
suffix = ""
|
||||
if not self.config.runtime_dryrun:
|
||||
if not self.config.Runtime_Options_dryrun:
|
||||
# rename the file
|
||||
try:
|
||||
ca.rename(utils.unique_file(full_path))
|
||||
@ -526,7 +526,7 @@ class CLI:
|
||||
filename_path = ca.path
|
||||
new_file = filename_path.with_suffix(".cbz")
|
||||
|
||||
if self.config.runtime_abort_on_conflict and new_file.exists():
|
||||
if self.config.Runtime_Options_abort_on_conflict and new_file.exists():
|
||||
print(msg_hdr + f"{new_file.name} already exists in the that folder.")
|
||||
return
|
||||
|
||||
@ -534,10 +534,10 @@ class CLI:
|
||||
|
||||
delete_success = False
|
||||
export_success = False
|
||||
if not self.config.runtime_dryrun:
|
||||
if not self.config.Runtime_Options_dryrun:
|
||||
if ca.export_as_zip(new_file):
|
||||
export_success = True
|
||||
if self.config.runtime_delete_after_zip_export:
|
||||
if self.config.Runtime_Options_delete_after_zip_export:
|
||||
try:
|
||||
filename_path.unlink(missing_ok=True)
|
||||
delete_success = True
|
||||
@ -549,7 +549,7 @@ class CLI:
|
||||
new_file.unlink(missing_ok=True)
|
||||
else:
|
||||
msg = msg_hdr + f"Dry-run: Would try to create {os.path.split(new_file)[1]}"
|
||||
if self.config.runtime_delete_after_zip_export:
|
||||
if self.config.Runtime_Options_delete_after_zip_export:
|
||||
msg += " and delete original."
|
||||
print(msg)
|
||||
return
|
||||
@ -557,7 +557,7 @@ class CLI:
|
||||
msg = msg_hdr
|
||||
if export_success:
|
||||
msg += f"Archive exported successfully to: {os.path.split(new_file)[1]}"
|
||||
if self.config.runtime_delete_after_zip_export and delete_success:
|
||||
if self.config.Runtime_Options_delete_after_zip_export and delete_success:
|
||||
msg += " (Original deleted) "
|
||||
else:
|
||||
msg += "Archive failed to export!"
|
||||
@ -576,28 +576,28 @@ class CLI:
|
||||
return
|
||||
|
||||
if not ca.is_writable() and (
|
||||
self.config.commands_delete
|
||||
or self.config.commands_copy
|
||||
or self.config.commands_save
|
||||
or self.config.commands_rename
|
||||
self.config.Commands_delete
|
||||
or self.config.Commands_copy
|
||||
or self.config.Commands_save
|
||||
or self.config.Commands_rename
|
||||
):
|
||||
logger.error("This archive is not writable")
|
||||
return
|
||||
|
||||
if self.config.commands_print:
|
||||
if self.config.Commands_print:
|
||||
self.print(ca)
|
||||
|
||||
elif self.config.commands_delete:
|
||||
elif self.config.Commands_delete:
|
||||
self.delete(ca)
|
||||
|
||||
elif self.config.commands_copy is not None:
|
||||
elif self.config.Commands_copy is not None:
|
||||
self.copy(ca)
|
||||
|
||||
elif self.config.commands_save:
|
||||
elif self.config.Commands_save:
|
||||
self.save(ca, match_results)
|
||||
|
||||
elif self.config.commands_rename:
|
||||
elif self.config.Commands_rename:
|
||||
self.rename(ca)
|
||||
|
||||
elif self.config.commands_export_to_zip:
|
||||
elif self.config.Commands_export_to_zip:
|
||||
self.export(ca)
|
||||
|
@ -6,7 +6,7 @@ from comictaggerlib.ctsettings.commandline import (
|
||||
validate_commandline_settings,
|
||||
)
|
||||
from comictaggerlib.ctsettings.file import register_file_settings, validate_file_settings
|
||||
from comictaggerlib.ctsettings.plugin import register_plugin_settings, validate_plugin_settings
|
||||
from comictaggerlib.ctsettings.plugin import group_for_plugin, register_plugin_settings, validate_plugin_settings
|
||||
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
|
||||
from comictaggerlib.ctsettings.types import ComicTaggerPaths
|
||||
from comictalker import ComicTalker
|
||||
@ -23,4 +23,5 @@ __all__ = [
|
||||
"validate_plugin_settings",
|
||||
"ComicTaggerPaths",
|
||||
"ct_ns",
|
||||
"group_for_plugin",
|
||||
]
|
||||
|
@ -236,61 +236,72 @@ def register_commands(parser: settngs.Manager) -> None:
|
||||
parser.add_setting(
|
||||
"--only-set-cv-key",
|
||||
action="store_true",
|
||||
help="Only set the Comic Vine API key and quit.\n\n",
|
||||
help="Only set the Comic Vine API key and quit.",
|
||||
file=False,
|
||||
)
|
||||
parser.add_setting(
|
||||
"--list-plugins",
|
||||
action="store_true",
|
||||
help="List the available plugins.\n\n",
|
||||
file=False,
|
||||
)
|
||||
|
||||
|
||||
def register_commandline_settings(parser: settngs.Manager) -> None:
|
||||
parser.add_group("commands", register_commands, True)
|
||||
parser.add_persistent_group("runtime", register_runtime)
|
||||
parser.add_group("Commands", register_commands, True)
|
||||
parser.add_persistent_group("Runtime Options", register_runtime)
|
||||
|
||||
|
||||
def validate_commandline_settings(config: settngs.Config[ct_ns], parser: settngs.Manager) -> settngs.Config[ct_ns]:
|
||||
if config[0].commands_version:
|
||||
if config[0].Commands_version:
|
||||
parser.exit(
|
||||
status=1,
|
||||
message=f"ComicTagger {ctversion.version}: Copyright (c) 2012-2022 ComicTagger Team\n"
|
||||
"Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n",
|
||||
)
|
||||
|
||||
config[0].runtime_no_gui = any(
|
||||
config[0].Runtime_Options_no_gui = any(
|
||||
[
|
||||
config[0].commands_print,
|
||||
config[0].commands_delete,
|
||||
config[0].commands_save,
|
||||
config[0].commands_copy,
|
||||
config[0].commands_rename,
|
||||
config[0].commands_export_to_zip,
|
||||
config[0].commands_only_set_cv_key,
|
||||
config[0].runtime_no_gui,
|
||||
config[0].Commands_print,
|
||||
config[0].Commands_delete,
|
||||
config[0].Commands_save,
|
||||
config[0].Commands_copy,
|
||||
config[0].Commands_rename,
|
||||
config[0].Commands_export_to_zip,
|
||||
config[0].Commands_only_set_cv_key,
|
||||
config[0].Commands_list_plugins,
|
||||
config[0].Runtime_Options_no_gui,
|
||||
]
|
||||
)
|
||||
|
||||
if platform.system() == "Windows" and config[0].runtime_glob:
|
||||
if platform.system() == "Windows" and config[0].Runtime_Options_glob:
|
||||
# no globbing on windows shell, so do it for them
|
||||
import glob
|
||||
|
||||
globs = config[0].runtime_files
|
||||
config[0].runtime_files = []
|
||||
globs = config[0].Runtime_Options_files
|
||||
config[0].Runtime_Options_files = []
|
||||
for item in globs:
|
||||
config[0].runtime_files.extend(glob.glob(item))
|
||||
config[0].Runtime_Options_files.extend(glob.glob(item))
|
||||
|
||||
if not config[0].commands_only_set_cv_key and config[0].runtime_no_gui and not config[0].runtime_files:
|
||||
if (
|
||||
not config[0].Commands_only_set_cv_key
|
||||
and config[0].Runtime_Options_no_gui
|
||||
and not config[0].Runtime_Options_files
|
||||
):
|
||||
parser.exit(message="Command requires at least one filename!\n", status=1)
|
||||
|
||||
if config[0].commands_delete and not config[0].runtime_type:
|
||||
if config[0].Commands_delete and not config[0].Runtime_Options_type:
|
||||
parser.exit(message="Please specify the type to delete with -t\n", status=1)
|
||||
|
||||
if config[0].commands_save and not config[0].runtime_type:
|
||||
if config[0].Commands_save and not config[0].Runtime_Options_type:
|
||||
parser.exit(message="Please specify the type to save with -t\n", status=1)
|
||||
|
||||
if config[0].commands_copy:
|
||||
if not config[0].runtime_type:
|
||||
if config[0].Commands_copy:
|
||||
if not config[0].Runtime_Options_type:
|
||||
parser.exit(message="Please specify the type to copy to with -t\n", status=1)
|
||||
|
||||
if config[0].runtime_recursive:
|
||||
config[0].runtime_files = utils.get_recursive_filelist(config[0].runtime_files)
|
||||
if config[0].Runtime_Options_recursive:
|
||||
config[0].Runtime_Options_files = utils.get_recursive_filelist(config[0].Runtime_Options_files)
|
||||
|
||||
# take a crack at finding rar exe if it's not in the path
|
||||
if not utils.which("rar"):
|
||||
|
@ -123,7 +123,11 @@ 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(
|
||||
"--source",
|
||||
default="comicvine",
|
||||
help="Use a specified source by source ID (use --list-plugins to list all sources)",
|
||||
)
|
||||
parser.add_setting(
|
||||
"--remove-html-tables",
|
||||
default=False,
|
||||
@ -219,7 +223,7 @@ def autotag(parser: settngs.Manager) -> None:
|
||||
def validate_file_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
|
||||
new_filter = []
|
||||
remove = []
|
||||
for x in config[0].identifier_publisher_filter:
|
||||
for x in config[0].Issue_Identifier_publisher_filter:
|
||||
x = x.strip()
|
||||
if x: # ignore empty arguments
|
||||
if x[-1] == "-": # this publisher needs to be removed. We remove after all publishers have been enumerated
|
||||
@ -230,22 +234,22 @@ def validate_file_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_n
|
||||
for x in remove: # remove publishers
|
||||
if x in new_filter:
|
||||
new_filter.remove(x)
|
||||
config[0].identifier_publisher_filter = new_filter
|
||||
config[0].Issue_Identifier_publisher_filter = new_filter
|
||||
|
||||
config[0].rename_replacements = Replacements(
|
||||
[Replacement(x[0], x[1], x[2]) for x in config[0].rename_replacements[0]],
|
||||
[Replacement(x[0], x[1], x[2]) for x in config[0].rename_replacements[1]],
|
||||
config[0].File_Rename_replacements = Replacements(
|
||||
[Replacement(x[0], x[1], x[2]) for x in config[0].File_Rename_replacements[0]],
|
||||
[Replacement(x[0], x[1], x[2]) for x in config[0].File_Rename_replacements[1]],
|
||||
)
|
||||
return config
|
||||
|
||||
|
||||
def register_file_settings(parser: settngs.Manager) -> None:
|
||||
parser.add_group("general", general, False)
|
||||
parser.add_group("internal", internal, False)
|
||||
parser.add_group("identifier", identifier, False)
|
||||
parser.add_group("dialog", dialog, False)
|
||||
parser.add_group("filename", filename, False)
|
||||
parser.add_group("talker", talker, False)
|
||||
parser.add_group("cbl", cbl, False)
|
||||
parser.add_group("rename", rename, False)
|
||||
parser.add_group("autotag", autotag, False)
|
||||
parser.add_group("Issue Identifier", identifier, False)
|
||||
parser.add_group("Filename Parsing", filename, False)
|
||||
parser.add_group("Sources", talker, False)
|
||||
parser.add_group("Comic Book Lover", cbl, False)
|
||||
parser.add_group("File Rename", rename, False)
|
||||
parser.add_group("Auto-Tag", autotag, False)
|
||||
parser.add_group("General", general, False)
|
||||
parser.add_group("Dialog Flags", dialog, False)
|
||||
|
@ -7,12 +7,23 @@ from typing import cast
|
||||
import settngs
|
||||
|
||||
import comicapi.comicarchive
|
||||
import comicapi.utils
|
||||
import comictaggerlib.ctsettings
|
||||
from comicapi.comicarchive import Archiver
|
||||
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
|
||||
from comictalker.comictalker import ComicTalker
|
||||
|
||||
logger = logging.getLogger("comictagger")
|
||||
|
||||
|
||||
def group_for_plugin(plugin: Archiver | ComicTalker) -> str:
|
||||
if isinstance(plugin, ComicTalker):
|
||||
return f"Source {plugin.id}"
|
||||
if isinstance(plugin, Archiver):
|
||||
return "Archive"
|
||||
raise NotImplementedError(f"Invalid plugin received: {plugin=}")
|
||||
|
||||
|
||||
def archiver(manager: settngs.Manager) -> None:
|
||||
for archiver in comicapi.comicarchive.archivers:
|
||||
if archiver.exe:
|
||||
@ -26,27 +37,28 @@ def archiver(manager: settngs.Manager) -> None:
|
||||
|
||||
|
||||
def register_talker_settings(manager: settngs.Manager) -> None:
|
||||
for talker_id, talker in comictaggerlib.ctsettings.talkers.items():
|
||||
for talker in comictaggerlib.ctsettings.talkers.values():
|
||||
|
||||
def api_options(manager: settngs.Manager) -> None:
|
||||
# The default needs to be unset or None.
|
||||
# This allows this setting to be unset with the empty string, allowing the default to change
|
||||
manager.add_setting(
|
||||
f"--{talker_id}-key",
|
||||
f"--{talker.id}-key",
|
||||
display_name="API Key",
|
||||
help=f"API Key for {talker.name} (default: {talker.default_api_key})",
|
||||
)
|
||||
manager.add_setting(
|
||||
f"--{talker_id}-url",
|
||||
f"--{talker.id}-url",
|
||||
display_name="URL",
|
||||
help=f"URL for {talker.name} (default: {talker.default_api_url})",
|
||||
)
|
||||
|
||||
try:
|
||||
manager.add_persistent_group("talker_" + talker_id, api_options, False)
|
||||
manager.add_persistent_group("talker_" + talker_id, talker.register_settings, False)
|
||||
manager.add_persistent_group(group_for_plugin(talker), api_options, False)
|
||||
if hasattr(talker, "register_settings"):
|
||||
manager.add_persistent_group(group_for_plugin(talker), talker.register_settings, False)
|
||||
except Exception:
|
||||
logger.exception("Failed to register settings for %s", talker_id)
|
||||
logger.exception("Failed to register settings for %s", talker.id)
|
||||
|
||||
|
||||
def validate_archive_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
|
||||
@ -55,11 +67,11 @@ def validate_archive_settings(config: settngs.Config[ct_ns]) -> settngs.Config[c
|
||||
cfg = settngs.normalize_config(config, file=True, cmdline=True, default=False)
|
||||
for archiver in comicapi.comicarchive.archivers:
|
||||
exe_name = settngs.sanitize_name(archiver.exe)
|
||||
if exe_name in cfg[0]["archiver"] and cfg[0]["archiver"][exe_name]:
|
||||
if os.path.basename(cfg[0]["archiver"][exe_name]) == archiver.exe:
|
||||
comicapi.utils.add_to_path(os.path.dirname(cfg[0]["archiver"][exe_name]))
|
||||
if exe_name in cfg[0][group_for_plugin(archiver())] and cfg[0][group_for_plugin(archiver())][exe_name]:
|
||||
if os.path.basename(cfg[0][group_for_plugin(archiver())][exe_name]) == archiver.exe:
|
||||
comicapi.utils.add_to_path(os.path.dirname(cfg[0][group_for_plugin(archiver())][exe_name]))
|
||||
else:
|
||||
archiver.exe = cfg[0]["archiver"][exe_name]
|
||||
archiver.exe = cfg[0][group_for_plugin(archiver())][exe_name]
|
||||
|
||||
return config
|
||||
|
||||
@ -67,12 +79,12 @@ def validate_archive_settings(config: settngs.Config[ct_ns]) -> settngs.Config[c
|
||||
def validate_talker_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
|
||||
# Apply talker settings from config file
|
||||
cfg = settngs.normalize_config(config, True, True)
|
||||
for talker_id, talker in list(comictaggerlib.ctsettings.talkers.items()):
|
||||
for talker in list(comictaggerlib.ctsettings.talkers.values()):
|
||||
try:
|
||||
cfg[0]["talker_" + talker_id] = talker.parse_settings(cfg[0]["talker_" + talker_id])
|
||||
cfg[0][group_for_plugin(talker)] = talker.parse_settings(cfg[0][group_for_plugin(talker)])
|
||||
except Exception as e:
|
||||
# Remove talker as we failed to apply the settings
|
||||
del comictaggerlib.ctsettings.talkers[talker_id]
|
||||
del comictaggerlib.ctsettings.talkers[talker.id]
|
||||
logger.exception("Failed to initialize talker settings: %s", e)
|
||||
|
||||
return cast(settngs.Config[ct_ns], settngs.get_namespace(cfg, file=True, cmdline=True))
|
||||
@ -85,5 +97,5 @@ def validate_plugin_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct
|
||||
|
||||
|
||||
def register_plugin_settings(manager: settngs.Manager) -> None:
|
||||
manager.add_persistent_group("archiver", archiver, False)
|
||||
manager.add_persistent_group("Archive", archiver, False)
|
||||
register_talker_settings(manager)
|
||||
|
@ -8,40 +8,39 @@ import comictaggerlib.defaults
|
||||
|
||||
|
||||
class settngs_namespace(settngs.TypedNS):
|
||||
commands_version: bool
|
||||
commands_print: bool
|
||||
commands_delete: bool
|
||||
commands_copy: int
|
||||
commands_save: bool
|
||||
commands_rename: bool
|
||||
commands_export_to_zip: bool
|
||||
commands_only_set_cv_key: bool
|
||||
Commands_version: bool
|
||||
Commands_print: bool
|
||||
Commands_delete: bool
|
||||
Commands_copy: int
|
||||
Commands_save: bool
|
||||
Commands_rename: bool
|
||||
Commands_export_to_zip: bool
|
||||
Commands_only_set_cv_key: bool
|
||||
Commands_list_plugins: bool
|
||||
|
||||
runtime_config: comictaggerlib.ctsettings.types.ComicTaggerPaths
|
||||
runtime_verbose: int
|
||||
runtime_abort_on_conflict: bool
|
||||
runtime_delete_after_zip_export: bool
|
||||
runtime_parse_filename: bool
|
||||
runtime_issue_id: str
|
||||
runtime_online: bool
|
||||
runtime_metadata: comicapi.genericmetadata.GenericMetadata
|
||||
runtime_interactive: bool
|
||||
runtime_abort_on_low_confidence: bool
|
||||
runtime_summary: bool
|
||||
runtime_raw: bool
|
||||
runtime_recursive: bool
|
||||
runtime_script: str
|
||||
runtime_split_words: bool
|
||||
runtime_dryrun: bool
|
||||
runtime_darkmode: bool
|
||||
runtime_glob: bool
|
||||
runtime_quiet: bool
|
||||
runtime_type: list[int]
|
||||
runtime_overwrite: bool
|
||||
runtime_no_gui: bool
|
||||
runtime_files: list[str]
|
||||
|
||||
general_check_for_new_version: bool
|
||||
Runtime_Options_config: comictaggerlib.ctsettings.types.ComicTaggerPaths
|
||||
Runtime_Options_verbose: int
|
||||
Runtime_Options_abort_on_conflict: bool
|
||||
Runtime_Options_delete_after_zip_export: bool
|
||||
Runtime_Options_parse_filename: bool
|
||||
Runtime_Options_issue_id: str
|
||||
Runtime_Options_online: bool
|
||||
Runtime_Options_metadata: comicapi.genericmetadata.GenericMetadata
|
||||
Runtime_Options_interactive: bool
|
||||
Runtime_Options_abort_on_low_confidence: bool
|
||||
Runtime_Options_summary: bool
|
||||
Runtime_Options_raw: bool
|
||||
Runtime_Options_recursive: bool
|
||||
Runtime_Options_script: str
|
||||
Runtime_Options_split_words: bool
|
||||
Runtime_Options_dryrun: bool
|
||||
Runtime_Options_darkmode: bool
|
||||
Runtime_Options_glob: bool
|
||||
Runtime_Options_quiet: bool
|
||||
Runtime_Options_type: list[int]
|
||||
Runtime_Options_overwrite: bool
|
||||
Runtime_Options_no_gui: bool
|
||||
Runtime_Options_files: list[str]
|
||||
|
||||
internal_install_id: str
|
||||
internal_save_data_style: int
|
||||
@ -56,50 +55,56 @@ class settngs_namespace(settngs.TypedNS):
|
||||
internal_sort_column: int
|
||||
internal_sort_direction: int
|
||||
|
||||
identifier_series_match_identify_thresh: int
|
||||
identifier_border_crop_percent: int
|
||||
identifier_publisher_filter: list[str]
|
||||
identifier_series_match_search_thresh: int
|
||||
identifier_clear_metadata_on_import: bool
|
||||
identifier_auto_imprint: bool
|
||||
identifier_sort_series_by_year: bool
|
||||
identifier_exact_series_matches_first: bool
|
||||
identifier_always_use_publisher_filter: bool
|
||||
identifier_clear_form_before_populating: bool
|
||||
Issue_Identifier_series_match_identify_thresh: int
|
||||
Issue_Identifier_border_crop_percent: int
|
||||
Issue_Identifier_publisher_filter: list[str]
|
||||
Issue_Identifier_series_match_search_thresh: int
|
||||
Issue_Identifier_clear_metadata_on_import: bool
|
||||
Issue_Identifier_auto_imprint: bool
|
||||
Issue_Identifier_sort_series_by_year: bool
|
||||
Issue_Identifier_exact_series_matches_first: bool
|
||||
Issue_Identifier_always_use_publisher_filter: bool
|
||||
Issue_Identifier_clear_form_before_populating: bool
|
||||
|
||||
dialog_show_disclaimer: bool
|
||||
dialog_dont_notify_about_this_version: str
|
||||
dialog_ask_about_usage_stats: bool
|
||||
Filename_Parsing_complicated_parser: bool
|
||||
Filename_Parsing_remove_c2c: bool
|
||||
Filename_Parsing_remove_fcbd: bool
|
||||
Filename_Parsing_remove_publisher: bool
|
||||
|
||||
filename_complicated_parser: bool
|
||||
filename_remove_c2c: bool
|
||||
filename_remove_fcbd: bool
|
||||
filename_remove_publisher: bool
|
||||
Sources_source: str
|
||||
Sources_remove_html_tables: bool
|
||||
|
||||
talker_source: str
|
||||
talker_remove_html_tables: bool
|
||||
Comic_Book_Lover_assume_lone_credit_is_primary: bool
|
||||
Comic_Book_Lover_copy_characters_to_tags: bool
|
||||
Comic_Book_Lover_copy_teams_to_tags: bool
|
||||
Comic_Book_Lover_copy_locations_to_tags: bool
|
||||
Comic_Book_Lover_copy_storyarcs_to_tags: bool
|
||||
Comic_Book_Lover_copy_notes_to_comments: bool
|
||||
Comic_Book_Lover_copy_weblink_to_comments: bool
|
||||
Comic_Book_Lover_apply_transform_on_import: bool
|
||||
Comic_Book_Lover_apply_transform_on_bulk_operation: bool
|
||||
|
||||
cbl_assume_lone_credit_is_primary: bool
|
||||
cbl_copy_characters_to_tags: bool
|
||||
cbl_copy_teams_to_tags: bool
|
||||
cbl_copy_locations_to_tags: bool
|
||||
cbl_copy_storyarcs_to_tags: bool
|
||||
cbl_copy_notes_to_comments: bool
|
||||
cbl_copy_weblink_to_comments: bool
|
||||
cbl_apply_transform_on_import: bool
|
||||
cbl_apply_transform_on_bulk_operation: bool
|
||||
File_Rename_template: str
|
||||
File_Rename_issue_number_padding: int
|
||||
File_Rename_use_smart_string_cleanup: bool
|
||||
File_Rename_set_extension_based_on_archive: bool
|
||||
File_Rename_dir: str
|
||||
File_Rename_move_to_dir: bool
|
||||
File_Rename_strict: bool
|
||||
File_Rename_replacements: comictaggerlib.defaults.Replacements
|
||||
|
||||
rename_template: str
|
||||
rename_issue_number_padding: int
|
||||
rename_use_smart_string_cleanup: bool
|
||||
rename_set_extension_based_on_archive: bool
|
||||
rename_dir: str
|
||||
rename_move_to_dir: bool
|
||||
rename_strict: bool
|
||||
rename_replacements: comictaggerlib.defaults.Replacements
|
||||
Auto_Tag_save_on_low_confidence: bool
|
||||
Auto_Tag_dont_use_year_when_identifying: bool
|
||||
Auto_Tag_assume_1_if_no_issue_num: bool
|
||||
Auto_Tag_ignore_leading_numbers_in_filename: bool
|
||||
Auto_Tag_remove_archive_after_successful_match: bool
|
||||
|
||||
autotag_save_on_low_confidence: bool
|
||||
autotag_dont_use_year_when_identifying: bool
|
||||
autotag_assume_1_if_no_issue_num: bool
|
||||
autotag_ignore_leading_numbers_in_filename: bool
|
||||
autotag_remove_archive_after_successful_match: bool
|
||||
General_check_for_new_version: bool
|
||||
|
||||
Dialog_Flags_show_disclaimer: bool
|
||||
Dialog_Flags_dont_notify_about_this_version: str
|
||||
Dialog_Flags_ask_about_usage_stats: bool
|
||||
|
||||
Source_comicvine_comicvine_key: str
|
||||
Source_comicvine_comicvine_url: str
|
||||
Source_comicvine_cv_use_series_start_as_volume: bool
|
||||
|
@ -96,7 +96,7 @@ def open_tagger_window(
|
||||
) -> None:
|
||||
os.environ["QtWidgets.QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
||||
args = []
|
||||
if config[0].runtime_darkmode:
|
||||
if config[0].Runtime_Options_darkmode:
|
||||
args.extend(["-platform", "windows:darkmode=2"])
|
||||
args.extend(sys.argv)
|
||||
app = Application(args)
|
||||
@ -106,7 +106,7 @@ def open_tagger_window(
|
||||
raise SystemExit(1)
|
||||
|
||||
# needed to catch initial open file events (macOS)
|
||||
app.openFileRequest.connect(lambda x: config[0].runtime_files.append(x.toLocalFile()))
|
||||
app.openFileRequest.connect(lambda x: config[0].Runtime_Options_files.append(x.toLocalFile()))
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
# Set the MacOS dock icon
|
||||
@ -134,7 +134,7 @@ def open_tagger_window(
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
try:
|
||||
tagger_window = TaggerWindow(config[0].runtime_files, config, talkers)
|
||||
tagger_window = TaggerWindow(config[0].Runtime_Options_files, config, talkers)
|
||||
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
|
||||
tagger_window.show()
|
||||
|
||||
|
@ -96,10 +96,10 @@ class IssueIdentifier:
|
||||
|
||||
# used to eliminate series names that are too long based on our search
|
||||
# string
|
||||
self.series_match_thresh = config.identifier_series_match_identify_thresh
|
||||
self.series_match_thresh = config.Issue_Identifier_series_match_identify_thresh
|
||||
|
||||
# used to eliminate unlikely publishers
|
||||
self.publisher_filter = [s.strip().casefold() for s in config.identifier_publisher_filter]
|
||||
self.publisher_filter = [s.strip().casefold() for s in config.Issue_Identifier_publisher_filter]
|
||||
|
||||
self.additional_metadata = GenericMetadata()
|
||||
self.output_function: Callable[[str], None] = IssueIdentifier.default_write_output
|
||||
@ -239,10 +239,10 @@ class IssueIdentifier:
|
||||
|
||||
# try to get some metadata from filename
|
||||
md_from_filename = ca.metadata_from_filename(
|
||||
self.config.filename_complicated_parser,
|
||||
self.config.filename_remove_c2c,
|
||||
self.config.filename_remove_fcbd,
|
||||
self.config.filename_remove_publisher,
|
||||
self.config.Filename_Parsing_complicated_parser,
|
||||
self.config.Filename_Parsing_remove_c2c,
|
||||
self.config.Filename_Parsing_remove_fcbd,
|
||||
self.config.Filename_Parsing_remove_publisher,
|
||||
)
|
||||
|
||||
working_md = md_from_filename.copy()
|
||||
@ -291,7 +291,7 @@ class IssueIdentifier:
|
||||
return Score(score=0, url="", hash=0)
|
||||
|
||||
try:
|
||||
url_image_data = ImageFetcher(self.config.runtime_config.user_cache_dir).fetch(
|
||||
url_image_data = ImageFetcher(self.config.Runtime_Options_config.user_cache_dir).fetch(
|
||||
primary_img_url, blocking=True
|
||||
)
|
||||
except ImageFetcherException as e:
|
||||
@ -313,7 +313,7 @@ class IssueIdentifier:
|
||||
if use_remote_alternates:
|
||||
for alt_url in alt_urls:
|
||||
try:
|
||||
alt_url_image_data = ImageFetcher(self.config.runtime_config.user_cache_dir).fetch(
|
||||
alt_url_image_data = ImageFetcher(self.config.Runtime_Options_config.user_cache_dir).fetch(
|
||||
alt_url, blocking=True
|
||||
)
|
||||
except ImageFetcherException as e:
|
||||
@ -499,7 +499,7 @@ class IssueIdentifier:
|
||||
if narrow_cover_hash is not None:
|
||||
hash_list.append(narrow_cover_hash)
|
||||
|
||||
cropped_border = self.crop_border(cover_image_data, self.config.identifier_border_crop_percent)
|
||||
cropped_border = self.crop_border(cover_image_data, self.config.Issue_Identifier_border_crop_percent)
|
||||
if cropped_border is not None:
|
||||
hash_list.append(self.calculate_hash(cropped_border))
|
||||
logger.info("Adding cropped cover to the hashlist")
|
||||
|
@ -52,7 +52,10 @@ class IssueSelectionWindow(QtWidgets.QDialog):
|
||||
uic.loadUi(ui_path / "issueselectionwindow.ui", self)
|
||||
|
||||
self.coverWidget = CoverImageWidget(
|
||||
self.coverImageContainer, CoverImageWidget.AltCoverMode, config.runtime_config.user_cache_dir, talker
|
||||
self.coverImageContainer,
|
||||
CoverImageWidget.AltCoverMode,
|
||||
config.Runtime_Options_config.user_cache_dir,
|
||||
talker,
|
||||
)
|
||||
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)
|
||||
gridlayout.addWidget(self.coverWidget)
|
||||
@ -95,7 +98,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
|
||||
self.imageIssuesSourceWidget = CoverImageWidget(
|
||||
self.imageIssuesSourceLogo,
|
||||
CoverImageWidget.URLMode,
|
||||
config.runtime_config.user_cache_dir,
|
||||
config.Runtime_Options_config.user_cache_dir,
|
||||
talker,
|
||||
False,
|
||||
)
|
||||
|
@ -91,7 +91,7 @@ def configure_locale() -> None:
|
||||
|
||||
|
||||
def update_publishers(config: settngs.Config[ct_ns]) -> None:
|
||||
json_file = config[0].runtime_config.user_config_dir / "publishers.json"
|
||||
json_file = config[0].Runtime_Options_config.user_config_dir / "publishers.json"
|
||||
if json_file.exists():
|
||||
try:
|
||||
comicapi.utils.update_publishers(json.loads(json_file.read_text("utf-8")))
|
||||
@ -121,6 +121,18 @@ class App:
|
||||
comicapi.comicarchive.load_archive_plugins()
|
||||
ctsettings.talkers = comictalker.get_talkers(version, opts.config.user_cache_dir)
|
||||
|
||||
def list_plugins(
|
||||
self, talkers: list[comictalker.ComicTalker], archivers: list[type[comicapi.comicarchive.Archiver]]
|
||||
) -> None:
|
||||
print("Metadata Sources: (ID: Name URL)") # noqa: T201
|
||||
for talker in talkers:
|
||||
print(f"{talker.id}: {talker.name} {talker.default_api_url}") # noqa: T201
|
||||
|
||||
print("\nComic Archive: (Name: extension, exe)") # noqa: T201
|
||||
for archiver in archivers:
|
||||
a = archiver()
|
||||
print(f"{a.name()}: {a.extension()}, {a.exe}") # noqa: T201
|
||||
|
||||
def initialize(self) -> argparse.Namespace:
|
||||
conf, _ = self.initial_arg_parser.parse_known_args()
|
||||
assert conf is not None
|
||||
@ -141,7 +153,7 @@ class App:
|
||||
config_paths.user_config_dir / "settings.json", list(args) or None
|
||||
)
|
||||
config = cast(settngs.Config[ct_ns], self.manager.get_namespace(cfg, file=True, cmdline=True))
|
||||
config[0].runtime_config = config_paths
|
||||
config[0].Runtime_Options_config = config_paths
|
||||
|
||||
config = ctsettings.validate_commandline_settings(config, self.manager)
|
||||
config = ctsettings.validate_file_settings(config)
|
||||
@ -170,7 +182,7 @@ class App:
|
||||
|
||||
if len(talkers) < 1:
|
||||
error = error = (
|
||||
f"Failed to load any talkers, please re-install and check the log located in '{self.config[0].runtime_config.user_log_dir}' for more details",
|
||||
f"Failed to load any talkers, please re-install and check the log located in '{self.config[0].Runtime_Options_config.user_log_dir}' for more details",
|
||||
True,
|
||||
)
|
||||
|
||||
@ -183,34 +195,38 @@ class App:
|
||||
comicapi.utils.load_publishers()
|
||||
update_publishers(self.config)
|
||||
|
||||
if self.config[0].Commands_list_plugins:
|
||||
self.list_plugins(list(talkers.values()), comicapi.comicarchive.archivers)
|
||||
return
|
||||
|
||||
# manage the CV API key
|
||||
# None comparison is used so that the empty string can unset the value
|
||||
if not error and (
|
||||
self.config[0].talker_comicvine_comicvine_key is not None # type: ignore[attr-defined]
|
||||
or self.config[0].talker_comicvine_comicvine_url is not None # type: ignore[attr-defined]
|
||||
self.config[0].Source_comicvine_comicvine_key is not None
|
||||
or self.config[0].Source_comicvine_comicvine_url is not None
|
||||
):
|
||||
settings_path = self.config[0].runtime_config.user_config_dir / "settings.json"
|
||||
settings_path = self.config[0].Runtime_Options_config.user_config_dir / "settings.json"
|
||||
if self.config_load_success:
|
||||
self.manager.save_file(self.config[0], settings_path)
|
||||
|
||||
if self.config[0].commands_only_set_cv_key:
|
||||
if self.config[0].Commands_only_set_cv_key:
|
||||
if self.config_load_success:
|
||||
print("Key set") # noqa: T201
|
||||
return
|
||||
|
||||
if not self.config_load_success:
|
||||
error = (
|
||||
f"Failed to load settings, check the log located in '{self.config[0].runtime_config.user_log_dir}' for more details",
|
||||
f"Failed to load settings, check the log located in '{self.config[0].Runtime_Options_config.user_log_dir}' for more details",
|
||||
True,
|
||||
)
|
||||
|
||||
if not self.config[0].runtime_no_gui:
|
||||
if not self.config[0].Runtime_Options_no_gui:
|
||||
try:
|
||||
from comictaggerlib import gui
|
||||
|
||||
return gui.open_tagger_window(talkers, self.config, error)
|
||||
except ImportError:
|
||||
self.config[0].runtime_no_gui = True
|
||||
self.config[0].Runtime_Options_no_gui = True
|
||||
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
|
||||
|
||||
# GUI mode is not available or CLI mode was requested
|
||||
|
@ -45,7 +45,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
|
||||
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
|
||||
|
||||
self.altCoverWidget = CoverImageWidget(
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode, config.runtime_config.user_cache_dir, talker
|
||||
self.altCoverContainer, CoverImageWidget.AltCoverMode, config.Runtime_Options_config.user_cache_dir, talker
|
||||
)
|
||||
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
|
||||
gridlayout.addWidget(self.altCoverWidget)
|
||||
|
@ -62,32 +62,32 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
self.rename_list: list[str] = []
|
||||
|
||||
self.btnSettings.clicked.connect(self.modify_settings)
|
||||
platform = "universal" if self.config[0].rename_strict else "auto"
|
||||
self.renamer = FileRenamer(None, platform=platform, replacements=self.config[0].rename_replacements)
|
||||
platform = "universal" if self.config[0].File_Rename_strict else "auto"
|
||||
self.renamer = FileRenamer(None, platform=platform, replacements=self.config[0].File_Rename_replacements)
|
||||
|
||||
self.do_preview()
|
||||
|
||||
def config_renamer(self, ca: ComicArchive, md: GenericMetadata | None = None) -> str:
|
||||
self.renamer.set_template(self.config[0].rename_template)
|
||||
self.renamer.set_issue_zero_padding(self.config[0].rename_issue_number_padding)
|
||||
self.renamer.set_smart_cleanup(self.config[0].rename_use_smart_string_cleanup)
|
||||
self.renamer.replacements = self.config[0].rename_replacements
|
||||
self.renamer.set_template(self.config[0].File_Rename_template)
|
||||
self.renamer.set_issue_zero_padding(self.config[0].File_Rename_issue_number_padding)
|
||||
self.renamer.set_smart_cleanup(self.config[0].File_Rename_use_smart_string_cleanup)
|
||||
self.renamer.replacements = self.config[0].File_Rename_replacements
|
||||
|
||||
new_ext = ca.path.suffix # default
|
||||
if self.config[0].rename_set_extension_based_on_archive:
|
||||
if self.config[0].File_Rename_set_extension_based_on_archive:
|
||||
new_ext = ca.extension()
|
||||
|
||||
if md is None:
|
||||
md = ca.read_metadata(self.data_style)
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
self.config[0].filename_complicated_parser,
|
||||
self.config[0].filename_remove_c2c,
|
||||
self.config[0].filename_remove_fcbd,
|
||||
self.config[0].filename_remove_publisher,
|
||||
self.config[0].Filename_Parsing_complicated_parser,
|
||||
self.config[0].Filename_Parsing_remove_c2c,
|
||||
self.config[0].Filename_Parsing_remove_fcbd,
|
||||
self.config[0].Filename_Parsing_remove_publisher,
|
||||
)
|
||||
self.renamer.set_metadata(md)
|
||||
self.renamer.move = self.config[0].rename_move_to_dir
|
||||
self.renamer.move = self.config[0].File_Rename_move_to_dir
|
||||
return new_ext
|
||||
|
||||
def do_preview(self) -> None:
|
||||
@ -100,7 +100,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
try:
|
||||
new_name = self.renamer.determine_name(new_ext)
|
||||
except ValueError as e:
|
||||
logger.exception("Invalid format string: %s", self.config[0].rename_template)
|
||||
logger.exception("Invalid format string: %s", self.config[0].File_Rename_template)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
@ -114,7 +114,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
return
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
"Formatter failure: %s metadata: %s", self.config[0].rename_template, self.renamer.metadata
|
||||
"Formatter failure: %s metadata: %s", self.config[0].File_Rename_template, self.renamer.metadata
|
||||
)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
@ -190,7 +190,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
|
||||
folder = get_rename_dir(
|
||||
comic[0],
|
||||
self.config[0].rename_dir if self.config[0].rename_move_to_dir else None,
|
||||
self.config[0].File_Rename_dir if self.config[0].File_Rename_move_to_dir else None,
|
||||
)
|
||||
|
||||
full_path = folder / comic[1]
|
||||
|
@ -116,7 +116,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
uic.loadUi(ui_path / "seriesselectionwindow.ui", self)
|
||||
|
||||
self.imageWidget = CoverImageWidget(
|
||||
self.imageContainer, CoverImageWidget.URLMode, config.runtime_config.user_cache_dir, talker
|
||||
self.imageContainer, CoverImageWidget.URLMode, config.Runtime_Options_config.user_cache_dir, talker
|
||||
)
|
||||
gridlayout = QtWidgets.QGridLayout(self.imageContainer)
|
||||
gridlayout.addWidget(self.imageWidget)
|
||||
@ -161,7 +161,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
self.progdialog: QtWidgets.QProgressDialog | None = None
|
||||
self.search_thread: SearchThread | None = None
|
||||
|
||||
self.use_filter = self.config.identifier_always_use_publisher_filter
|
||||
self.use_filter = self.config.Issue_Identifier_always_use_publisher_filter
|
||||
|
||||
# Load to retrieve settings
|
||||
self.talker = talker
|
||||
@ -172,7 +172,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
self.imageSourceWidget = CoverImageWidget(
|
||||
self.imageSourceLogo,
|
||||
CoverImageWidget.URLMode,
|
||||
config.runtime_config.user_cache_dir,
|
||||
config.Runtime_Options_config.user_cache_dir,
|
||||
talker,
|
||||
False,
|
||||
)
|
||||
@ -356,7 +356,11 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
|
||||
def perform_query(self, refresh: bool = False) -> None:
|
||||
self.search_thread = SearchThread(
|
||||
self.talker, self.series_name, refresh, self.literal, self.config.identifier_series_match_search_thresh
|
||||
self.talker,
|
||||
self.series_name,
|
||||
refresh,
|
||||
self.literal,
|
||||
self.config.Issue_Identifier_series_match_search_thresh,
|
||||
)
|
||||
self.search_thread.searchComplete.connect(self.search_complete)
|
||||
self.search_thread.progressUpdate.connect(self.search_progress_update)
|
||||
@ -409,7 +413,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
# filter the publishers if enabled set
|
||||
if self.use_filter:
|
||||
try:
|
||||
publisher_filter = {s.strip().casefold() for s in self.config.identifier_publisher_filter}
|
||||
publisher_filter = {s.strip().casefold() for s in self.config.Issue_Identifier_publisher_filter}
|
||||
# use '' as publisher name if None
|
||||
self.series_list = dict(
|
||||
filter(
|
||||
@ -425,7 +429,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
# compare as str in case extra chars ie. '1976?'
|
||||
# - missing (none) values being converted to 'None' - consistent with prior behaviour in v1.2.3
|
||||
# sort by start_year if set
|
||||
if self.config.identifier_sort_series_by_year:
|
||||
if self.config.Issue_Identifier_sort_series_by_year:
|
||||
try:
|
||||
self.series_list = dict(
|
||||
natsort.natsorted(
|
||||
@ -445,7 +449,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
|
||||
logger.exception("bad data error sorting results by count_of_issues")
|
||||
|
||||
# move sanitized matches to the front
|
||||
if self.config.identifier_exact_series_matches_first:
|
||||
if self.config.Issue_Identifier_exact_series_matches_first:
|
||||
try:
|
||||
sanitized = utils.sanitize_title(self.series_name, False).casefold()
|
||||
sanitized_no_articles = utils.sanitize_title(self.series_name, True).casefold()
|
||||
|
@ -188,7 +188,7 @@ 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.sources = comictaggerlib.ui.talkeruigenerator.generate_source_option_tabs(
|
||||
self.tComicTalkers, self.config, self.talkers
|
||||
)
|
||||
self.connect_signals()
|
||||
@ -307,43 +307,45 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.leRarExePath.setText(getattr(self.config[0], self.config[1]["archiver"].v["rar"].internal_name))
|
||||
else:
|
||||
self.leRarExePath.setEnabled(False)
|
||||
self.sbNameMatchIdentifyThresh.setValue(self.config[0].identifier_series_match_identify_thresh)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config[0].identifier_series_match_search_thresh)
|
||||
self.tePublisherFilter.setPlainText("\n".join(self.config[0].identifier_publisher_filter))
|
||||
self.sbNameMatchIdentifyThresh.setValue(self.config[0].Issue_Identifier_series_match_identify_thresh)
|
||||
self.sbNameMatchSearchThresh.setValue(self.config[0].Issue_Identifier_series_match_search_thresh)
|
||||
self.tePublisherFilter.setPlainText("\n".join(self.config[0].Issue_Identifier_publisher_filter))
|
||||
|
||||
self.cbxCheckForNewVersion.setChecked(self.config[0].general_check_for_new_version)
|
||||
self.cbxCheckForNewVersion.setChecked(self.config[0].General_check_for_new_version)
|
||||
|
||||
self.cbxComplicatedParser.setChecked(self.config[0].filename_complicated_parser)
|
||||
self.cbxRemoveC2C.setChecked(self.config[0].filename_remove_c2c)
|
||||
self.cbxRemoveFCBD.setChecked(self.config[0].filename_remove_fcbd)
|
||||
self.cbxRemovePublisher.setChecked(self.config[0].filename_remove_publisher)
|
||||
self.cbxComplicatedParser.setChecked(self.config[0].Filename_Parsing_complicated_parser)
|
||||
self.cbxRemoveC2C.setChecked(self.config[0].Filename_Parsing_remove_c2c)
|
||||
self.cbxRemoveFCBD.setChecked(self.config[0].Filename_Parsing_remove_fcbd)
|
||||
self.cbxRemovePublisher.setChecked(self.config[0].Filename_Parsing_remove_publisher)
|
||||
self.switch_parser()
|
||||
|
||||
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.cbxClearFormBeforePopulating.setChecked(self.config[0].Issue_Identifier_clear_form_before_populating)
|
||||
self.cbxUseFilter.setChecked(self.config[0].Issue_Identifier_always_use_publisher_filter)
|
||||
self.cbxSortByYear.setChecked(self.config[0].Issue_Identifier_sort_series_by_year)
|
||||
self.cbxExactMatches.setChecked(self.config[0].Issue_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)
|
||||
self.cbxCopyTeamsToTags.setChecked(self.config[0].cbl_copy_teams_to_tags)
|
||||
self.cbxCopyLocationsToTags.setChecked(self.config[0].cbl_copy_locations_to_tags)
|
||||
self.cbxCopyStoryArcsToTags.setChecked(self.config[0].cbl_copy_storyarcs_to_tags)
|
||||
self.cbxCopyNotesToComments.setChecked(self.config[0].cbl_copy_notes_to_comments)
|
||||
self.cbxCopyWebLinkToComments.setChecked(self.config[0].cbl_copy_weblink_to_comments)
|
||||
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.config[0].cbl_apply_transform_on_import)
|
||||
self.cbxApplyCBLTransformOnBatchOperation.setChecked(self.config[0].cbl_apply_transform_on_bulk_operation)
|
||||
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.config[0].Comic_Book_Lover_assume_lone_credit_is_primary)
|
||||
self.cbxCopyCharactersToTags.setChecked(self.config[0].Comic_Book_Lover_copy_characters_to_tags)
|
||||
self.cbxCopyTeamsToTags.setChecked(self.config[0].Comic_Book_Lover_copy_teams_to_tags)
|
||||
self.cbxCopyLocationsToTags.setChecked(self.config[0].Comic_Book_Lover_copy_locations_to_tags)
|
||||
self.cbxCopyStoryArcsToTags.setChecked(self.config[0].Comic_Book_Lover_copy_storyarcs_to_tags)
|
||||
self.cbxCopyNotesToComments.setChecked(self.config[0].Comic_Book_Lover_copy_notes_to_comments)
|
||||
self.cbxCopyWebLinkToComments.setChecked(self.config[0].Comic_Book_Lover_copy_weblink_to_comments)
|
||||
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.config[0].Comic_Book_Lover_apply_transform_on_import)
|
||||
self.cbxApplyCBLTransformOnBatchOperation.setChecked(
|
||||
self.config[0].Comic_Book_Lover_apply_transform_on_bulk_operation
|
||||
)
|
||||
|
||||
self.leRenameTemplate.setText(self.config[0].rename_template)
|
||||
self.leIssueNumPadding.setText(str(self.config[0].rename_issue_number_padding))
|
||||
self.cbxSmartCleanup.setChecked(self.config[0].rename_use_smart_string_cleanup)
|
||||
self.cbxChangeExtension.setChecked(self.config[0].rename_set_extension_based_on_archive)
|
||||
self.cbxMoveFiles.setChecked(self.config[0].rename_move_to_dir)
|
||||
self.leDirectory.setText(self.config[0].rename_dir)
|
||||
self.cbxRenameStrict.setChecked(self.config[0].rename_strict)
|
||||
self.leRenameTemplate.setText(self.config[0].File_Rename_template)
|
||||
self.leIssueNumPadding.setText(str(self.config[0].File_Rename_issue_number_padding))
|
||||
self.cbxSmartCleanup.setChecked(self.config[0].File_Rename_use_smart_string_cleanup)
|
||||
self.cbxChangeExtension.setChecked(self.config[0].File_Rename_set_extension_based_on_archive)
|
||||
self.cbxMoveFiles.setChecked(self.config[0].File_Rename_move_to_dir)
|
||||
self.leDirectory.setText(self.config[0].File_Rename_dir)
|
||||
self.cbxRenameStrict.setChecked(self.config[0].File_Rename_strict)
|
||||
|
||||
for table, replacments in zip(
|
||||
(self.twLiteralReplacements, self.twValueReplacements), self.config[0].rename_replacements
|
||||
(self.twLiteralReplacements, self.twValueReplacements), self.config[0].File_Rename_replacements
|
||||
):
|
||||
table.clearContents()
|
||||
for i in reversed(range(table.rowCount())):
|
||||
@ -383,7 +385,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.rename_test()
|
||||
if self.rename_error is not None:
|
||||
if isinstance(self.rename_error, ValueError):
|
||||
logger.exception("Invalid format string: %s", self.config[0].rename_template)
|
||||
logger.exception("Invalid format string: %s", self.config[0].File_Rename_template)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
@ -397,7 +399,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
return
|
||||
else:
|
||||
logger.exception(
|
||||
"Formatter failure: %s metadata: %s", self.config[0].rename_template, self.renamer.metadata
|
||||
"Formatter failure: %s metadata: %s", self.config[0].File_Rename_template, self.renamer.metadata
|
||||
)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
@ -420,48 +422,50 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
if not str(self.leIssueNumPadding.text()).isdigit():
|
||||
self.leIssueNumPadding.setText("0")
|
||||
|
||||
self.config[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked()
|
||||
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].identifier_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
|
||||
self.config[0].identifier_publisher_filter = utils.split(self.tePublisherFilter.toPlainText(), "\n")
|
||||
self.config[0].Issue_Identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
|
||||
self.config[0].Issue_Identifier_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
|
||||
self.config[0].Issue_Identifier_publisher_filter = utils.split(self.tePublisherFilter.toPlainText(), "\n")
|
||||
|
||||
self.config[0].filename_complicated_parser = self.cbxComplicatedParser.isChecked()
|
||||
self.config[0].filename_remove_c2c = self.cbxRemoveC2C.isChecked()
|
||||
self.config[0].filename_remove_fcbd = self.cbxRemoveFCBD.isChecked()
|
||||
self.config[0].filename_remove_publisher = self.cbxRemovePublisher.isChecked()
|
||||
self.config[0].Filename_Parsing_complicated_parser = self.cbxComplicatedParser.isChecked()
|
||||
self.config[0].Filename_Parsing_remove_c2c = self.cbxRemoveC2C.isChecked()
|
||||
self.config[0].Filename_Parsing_remove_fcbd = self.cbxRemoveFCBD.isChecked()
|
||||
self.config[0].Filename_Parsing_remove_publisher = self.cbxRemovePublisher.isChecked()
|
||||
|
||||
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].Issue_Identifier_clear_form_before_populating = self.cbxClearFormBeforePopulating.isChecked()
|
||||
self.config[0].Issue_Identifier_always_use_publisher_filter = self.cbxUseFilter.isChecked()
|
||||
self.config[0].Issue_Identifier_sort_series_by_year = self.cbxSortByYear.isChecked()
|
||||
self.config[0].Issue_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()
|
||||
self.config[0].cbl_copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
|
||||
self.config[0].cbl_copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
|
||||
self.config[0].cbl_copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
|
||||
self.config[0].cbl_copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
|
||||
self.config[0].cbl_copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
|
||||
self.config[0].cbl_apply_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
|
||||
self.config[0].cbl_apply_transform_on_bulk_operation = self.cbxApplyCBLTransformOnBatchOperation.isChecked()
|
||||
self.config[0].Comic_Book_Lover_assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
|
||||
self.config[0].Comic_Book_Lover_copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
|
||||
self.config[0].Comic_Book_Lover_apply_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
|
||||
self.config.values.Comic_Book_Lover_apply_transform_on_bulk_operation = (
|
||||
self.cbxApplyCBLTransformOnBatchOperation.isChecked()
|
||||
)
|
||||
|
||||
self.config[0].rename_template = str(self.leRenameTemplate.text())
|
||||
self.config[0].rename_issue_number_padding = int(self.leIssueNumPadding.text())
|
||||
self.config[0].rename_use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()
|
||||
self.config[0].rename_set_extension_based_on_archive = self.cbxChangeExtension.isChecked()
|
||||
self.config[0].rename_move_to_dir = self.cbxMoveFiles.isChecked()
|
||||
self.config[0].rename_dir = self.leDirectory.text()
|
||||
self.config[0].File_Rename_template = str(self.leRenameTemplate.text())
|
||||
self.config[0].File_Rename_issue_number_padding = int(self.leIssueNumPadding.text())
|
||||
self.config[0].File_Rename_use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()
|
||||
self.config[0].File_Rename_set_extension_based_on_archive = self.cbxChangeExtension.isChecked()
|
||||
self.config[0].File_Rename_move_to_dir = self.cbxMoveFiles.isChecked()
|
||||
self.config[0].File_Rename_dir = self.leDirectory.text()
|
||||
|
||||
self.config[0].rename_strict = self.cbxRenameStrict.isChecked()
|
||||
self.config[0].rename_replacements = self.get_replacements()
|
||||
self.config[0].File_Rename_strict = self.cbxRenameStrict.isChecked()
|
||||
self.config[0].File_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")
|
||||
settngs.save_file(self.config, self.config[0].Runtime_Options_config.user_config_dir / "settings.json")
|
||||
self.parent().config = self.config
|
||||
QtWidgets.QDialog.accept(self)
|
||||
|
||||
@ -474,8 +478,8 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.select_file(self.leRarExePath, "RAR")
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
ImageFetcher(self.config[0].runtime_config.user_cache_dir).clear_cache()
|
||||
ComicCacher(self.config[0].runtime_config.user_cache_dir, version).clear_cache()
|
||||
ImageFetcher(self.config[0].Runtime_Options_config.user_cache_dir).clear_cache()
|
||||
ComicCacher(self.config[0].Runtime_Options_config.user_cache_dir, version).clear_cache()
|
||||
QtWidgets.QMessageBox.information(self, self.name, "Cache has been cleared.")
|
||||
|
||||
def reset_settings(self) -> None:
|
||||
|
@ -156,10 +156,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
|
||||
|
||||
if config[0].runtime_type and isinstance(config[0].runtime_type[0], int):
|
||||
if config[0].Runtime_Options_type and isinstance(config[0].Runtime_Options_type[0], int):
|
||||
# respect the command line option tag type
|
||||
config[0].internal_save_data_style = config[0].runtime_type[0]
|
||||
config[0].internal_load_data_style = config[0].runtime_type[0]
|
||||
config[0].internal_save_data_style = config[0].Runtime_Options_type[0]
|
||||
config[0].internal_load_data_style = config[0].Runtime_Options_type[0]
|
||||
|
||||
self.save_data_style = config[0].internal_save_data_style
|
||||
self.load_data_style = config[0].internal_load_data_style
|
||||
@ -245,7 +245,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if len(file_list) != 0:
|
||||
self.fileSelectionList.add_path_list(file_list)
|
||||
|
||||
if self.config[0].dialog_show_disclaimer:
|
||||
if self.config[0].Dialog_Flags_show_disclaimer:
|
||||
checked = OptionalMessageDialog.msg(
|
||||
self,
|
||||
"Welcome!",
|
||||
@ -264,15 +264,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
Have fun!
|
||||
""",
|
||||
)
|
||||
self.config[0].dialog_show_disclaimer = not checked
|
||||
self.config[0].Dialog_Flags_show_disclaimer = not checked
|
||||
|
||||
if self.config[0].general_check_for_new_version:
|
||||
if self.config[0].General_check_for_new_version:
|
||||
self.check_latest_version_online()
|
||||
|
||||
def current_talker(self) -> ComicTalker:
|
||||
if self.config[0].talker_source in self.talkers:
|
||||
return self.talkers[self.config[0].talker_source]
|
||||
logger.error("Could not find the '%s' talker", self.config[0].talker_source)
|
||||
if self.config[0].Sources_source in self.talkers:
|
||||
return self.talkers[self.config[0].Sources_source]
|
||||
logger.error("Could not find the '%s' talker", self.config[0].Sources_source)
|
||||
raise SystemExit(2)
|
||||
|
||||
def open_file_event(self, url: QtCore.QUrl) -> None:
|
||||
@ -285,7 +285,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def setup_logger(self) -> ApplicationLogWindow:
|
||||
try:
|
||||
current_logs = (self.config[0].runtime_config.user_log_dir / "ComicTagger.log").read_text("utf-8")
|
||||
current_logs = (self.config[0].Runtime_Options_config.user_log_dir / "ComicTagger.log").read_text("utf-8")
|
||||
except Exception:
|
||||
current_logs = ""
|
||||
root_logger = logging.getLogger()
|
||||
@ -618,10 +618,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
def actual_load_current_archive(self) -> None:
|
||||
if self.metadata.is_empty and self.comic_archive is not None:
|
||||
self.metadata = self.comic_archive.metadata_from_filename(
|
||||
self.config[0].filename_complicated_parser,
|
||||
self.config[0].filename_remove_c2c,
|
||||
self.config[0].filename_remove_fcbd,
|
||||
self.config[0].filename_remove_publisher,
|
||||
self.config[0].Filename_Parsing_complicated_parser,
|
||||
self.config[0].Filename_Parsing_remove_c2c,
|
||||
self.config[0].Filename_Parsing_remove_fcbd,
|
||||
self.config[0].Filename_Parsing_remove_publisher,
|
||||
)
|
||||
if len(self.metadata.pages) == 0 and self.comic_archive is not None:
|
||||
self.metadata.set_default_page_list(self.comic_archive.get_number_of_pages())
|
||||
@ -967,10 +967,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
# copy the form onto metadata object
|
||||
self.form_to_metadata()
|
||||
new_metadata = self.comic_archive.metadata_from_filename(
|
||||
self.config[0].filename_complicated_parser,
|
||||
self.config[0].filename_remove_c2c,
|
||||
self.config[0].filename_remove_fcbd,
|
||||
self.config[0].filename_remove_publisher,
|
||||
self.config[0].Filename_Parsing_complicated_parser,
|
||||
self.config[0].Filename_Parsing_remove_c2c,
|
||||
self.config[0].Filename_Parsing_remove_fcbd,
|
||||
self.config[0].Filename_Parsing_remove_publisher,
|
||||
split_words,
|
||||
)
|
||||
if new_metadata is not None:
|
||||
@ -1079,10 +1079,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
else:
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
if new_metadata is not None:
|
||||
if self.config[0].cbl_apply_transform_on_import:
|
||||
if self.config[0].Comic_Book_Lover_apply_transform_on_import:
|
||||
new_metadata = CBLTransformer(new_metadata, self.config[0]).apply()
|
||||
|
||||
if self.config[0].identifier_clear_form_before_populating:
|
||||
if self.config[0].Issue_Identifier_clear_form_before_populating:
|
||||
self.clear_form()
|
||||
|
||||
notes = (
|
||||
@ -1093,7 +1093,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
new_metadata.replace(
|
||||
notes=utils.combine_notes(self.metadata.notes, notes, "Tagged with ComicTagger"),
|
||||
description=cleanup_html(
|
||||
new_metadata.description, self.config[0].talker_remove_html_tables
|
||||
new_metadata.description, self.config[0].Sources_remove_html_tables
|
||||
),
|
||||
)
|
||||
)
|
||||
@ -1636,7 +1636,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if ca.has_metadata(src_style) and ca.is_writable():
|
||||
md = ca.read_metadata(src_style)
|
||||
|
||||
if dest_style == MetaDataStyle.CBI and self.config[0].cbl_apply_transform_on_bulk_operation:
|
||||
if (
|
||||
dest_style == MetaDataStyle.CBI
|
||||
and self.config[0].Comic_Book_Lover_apply_transform_on_bulk_operation
|
||||
):
|
||||
md = CBLTransformer(md, self.config[0]).apply()
|
||||
|
||||
if not ca.write_metadata(md, dest_style):
|
||||
@ -1674,7 +1677,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
logger.exception("Save aborted.")
|
||||
|
||||
if not ct_md.is_empty:
|
||||
if self.config[0].cbl_apply_transform_on_import:
|
||||
if self.config[0].Comic_Book_Lover_apply_transform_on_import:
|
||||
ct_md = CBLTransformer(ct_md, self.config[0]).apply()
|
||||
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
@ -1704,10 +1707,10 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
logger.error("Failed to load metadata for %s: %s", ca.path, e)
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
self.config[0].filename_complicated_parser,
|
||||
self.config[0].filename_remove_c2c,
|
||||
self.config[0].filename_remove_fcbd,
|
||||
self.config[0].filename_remove_publisher,
|
||||
self.config[0].Filename_Parsing_complicated_parser,
|
||||
self.config[0].Filename_Parsing_remove_c2c,
|
||||
self.config[0].Filename_Parsing_remove_fcbd,
|
||||
self.config[0].Filename_Parsing_remove_publisher,
|
||||
dlg.split_words,
|
||||
)
|
||||
if dlg.ignore_leading_digits_in_filename and md.series is not None:
|
||||
@ -1793,7 +1796,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
)
|
||||
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
|
||||
|
||||
if self.config[0].identifier_auto_imprint:
|
||||
if self.config[0].Issue_Identifier_auto_imprint:
|
||||
md.fix_publisher()
|
||||
|
||||
if not ca.write_metadata(md, self.save_data_style):
|
||||
@ -1979,7 +1982,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.config[0].internal_sort_column,
|
||||
self.config[0].internal_sort_direction,
|
||||
) = self.fileSelectionList.get_sorting()
|
||||
settngs.save_file(self.config, self.config[0].runtime_config.user_config_dir / "settings.json")
|
||||
settngs.save_file(self.config, self.config[0].Runtime_Options_config.user_config_dir / "settings.json")
|
||||
|
||||
event.accept()
|
||||
else:
|
||||
@ -2106,7 +2109,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.version_check_complete(version_checker.get_latest_version(self.config[0].internal_install_id))
|
||||
|
||||
def version_check_complete(self, new_version: tuple[str, str]) -> None:
|
||||
if new_version[0] not in (self.version, self.config[0].dialog_dont_notify_about_this_version):
|
||||
if new_version[0] not in (self.version, self.config[0].Dialog_Flags_dont_notify_about_this_version):
|
||||
website = "https://github.com/comictagger/comictagger"
|
||||
checked = OptionalMessageDialog.msg(
|
||||
self,
|
||||
@ -2117,7 +2120,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
"Don't tell me about this version again",
|
||||
)
|
||||
if checked:
|
||||
self.config[0].dialog_dont_notify_about_this_version = new_version[0]
|
||||
self.config[0].Dialog_Flags_dont_notify_about_this_version = new_version[0]
|
||||
|
||||
def on_incoming_socket_connection(self) -> None:
|
||||
# Accept connection from other instance.
|
||||
|
@ -2,12 +2,12 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import partial
|
||||
from typing import Any, NamedTuple
|
||||
from typing import Any, NamedTuple, cast
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
from comictaggerlib.ctsettings import ct_ns
|
||||
from comictaggerlib.ctsettings import ct_ns, group_for_plugin
|
||||
from comictaggerlib.graphics import graphics_path
|
||||
from comictalker.comictalker import ComicTalker
|
||||
|
||||
@ -16,9 +16,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class TalkerTab(NamedTuple):
|
||||
tab: QtWidgets.QWidget
|
||||
# dict[option.dest] = QWidget
|
||||
widgets: dict[str, QtWidgets.QWidget]
|
||||
|
||||
|
||||
class Sources(NamedTuple):
|
||||
cbx_sources: QtWidgets.QComboBox
|
||||
tabs: list[tuple[ComicTalker, TalkerTab]]
|
||||
|
||||
|
||||
class PasswordEdit(QtWidgets.QLineEdit):
|
||||
"""
|
||||
Password LineEdit with icons to show/hide password entries.
|
||||
@ -38,11 +44,11 @@ class PasswordEdit(QtWidgets.QLineEdit):
|
||||
# Add the password hide/shown toggle at the end of the edit box.
|
||||
self.togglepasswordAction = self.addAction(self.visibleIcon, QtWidgets.QLineEdit.TrailingPosition)
|
||||
self.togglepasswordAction.setToolTip("Show password")
|
||||
self.togglepasswordAction.triggered.connect(self.on_toggle_password_Action)
|
||||
self.togglepasswordAction.triggered.connect(self.on_toggle_password_action)
|
||||
|
||||
self.password_shown = False
|
||||
|
||||
def on_toggle_password_Action(self) -> None:
|
||||
def on_toggle_password_action(self) -> None:
|
||||
if not self.password_shown:
|
||||
self.setEchoMode(QtWidgets.QLineEdit.Normal)
|
||||
self.password_shown = True
|
||||
@ -56,14 +62,16 @@ class PasswordEdit(QtWidgets.QLineEdit):
|
||||
|
||||
|
||||
def generate_api_widgets(
|
||||
talker_id: str,
|
||||
sources: dict[str, QtWidgets.QWidget],
|
||||
config: settngs.Config[ct_ns],
|
||||
talker: ComicTalker,
|
||||
widgets: TalkerTab,
|
||||
key_option: settngs.Setting,
|
||||
url_option: settngs.Setting,
|
||||
layout: QtWidgets.QGridLayout,
|
||||
talkers: dict[str, ComicTalker],
|
||||
) -> None:
|
||||
# *args enforces keyword arguments and allows position arguments to be ignored
|
||||
def call_check_api(*args: Any, le_url: QtWidgets.QLineEdit, le_key: QtWidgets.QLineEdit, talker_id: str) -> None:
|
||||
def call_check_api(
|
||||
*args: Any, le_url: QtWidgets.QLineEdit, le_key: QtWidgets.QLineEdit, talker: ComicTalker
|
||||
) -> None:
|
||||
url = ""
|
||||
key = ""
|
||||
if le_key is not None:
|
||||
@ -71,46 +79,43 @@ def generate_api_widgets(
|
||||
if le_url is not None:
|
||||
url = le_url.text().strip()
|
||||
|
||||
check_text, check_bool = talkers[talker_id].check_api_key(url, key)
|
||||
check_text, check_bool = talker.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
|
||||
if key_option.file:
|
||||
# record the current row, so we know where to add the button
|
||||
btn_test_row = layout.rowCount()
|
||||
le_key = generate_password_textbox(talker_key, layout)
|
||||
le_key = generate_password_textbox(key_option, layout)
|
||||
|
||||
# To enable setting and getting
|
||||
sources["tabs"][talker_id].widgets[f"talker_{talker_id}_{talker_id}_key"] = le_key
|
||||
widgets.widgets[key_option.dest] = le_key
|
||||
|
||||
# only file settings are saved
|
||||
if talker_url.file:
|
||||
# record the current row so we know where to add the button
|
||||
if url_option.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)
|
||||
value, _ = settngs.get_option(config[0], talker_url)
|
||||
if not value:
|
||||
le_url.setText(talkers[talker_id].default_api_url)
|
||||
le_url = generate_textbox(url_option, layout)
|
||||
# We insert the default url here so that people don't think it's unset
|
||||
le_url.setText(talker.default_api_url)
|
||||
# To enable setting and getting
|
||||
sources["tabs"][talker_id].widgets[f"talker_{talker_id}_{talker_id}_url"] = le_url
|
||||
widgets.widgets[url_option.dest] = 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))
|
||||
btn.clicked.connect(partial(call_check_api, le_url=le_url, le_key=le_key, talker=talker))
|
||||
|
||||
|
||||
def generate_checkbox(option: settngs.Setting, layout: QtWidgets.QGridLayout) -> QtWidgets.QCheckBox:
|
||||
@ -171,31 +176,39 @@ def generate_password_textbox(option: settngs.Setting, layout: QtWidgets.QGridLa
|
||||
return widget
|
||||
|
||||
|
||||
def settings_to_talker_form(sources: dict[str, QtWidgets.QWidget], config: settngs.Config[ct_ns]) -> None:
|
||||
def settings_to_talker_form(sources: Sources, config: settngs.Config[ct_ns]) -> 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))
|
||||
sources[0].setCurrentIndex(sources[0].findData(config[0].Sources_source))
|
||||
|
||||
for talker in sources["tabs"].items():
|
||||
for name, widget in talker[1].widgets.items():
|
||||
value = getattr(config[0], name)
|
||||
value_type = type(value)
|
||||
# Iterate over the tabs, the talker is included in the tab so no extra lookup is needed
|
||||
for talker, tab in sources.tabs:
|
||||
# dest is guaranteed to be unique within a talker
|
||||
# and refer to the correct item in config.definitions.v['group name']
|
||||
for dest, widget in tab.widgets.items():
|
||||
value, default = settngs.get_option(config.values, config.definitions[group_for_plugin(talker)].v[dest])
|
||||
try:
|
||||
if value_type is str and value:
|
||||
if isinstance(value, str) and value and isinstance(widget, QtWidgets.QLineEdit) and not default:
|
||||
widget.setText(value)
|
||||
if value_type is int or value_type is float:
|
||||
if isinstance(value, (float, int)) and isinstance(
|
||||
widget, (QtWidgets.QSpinBox, QtWidgets.QDoubleSpinBox)
|
||||
):
|
||||
widget.setValue(value)
|
||||
if value_type is bool:
|
||||
if isinstance(value, bool) and isinstance(widget, QtWidgets.QCheckBox):
|
||||
widget.setChecked(value)
|
||||
except Exception:
|
||||
logger.debug("Failed to set value of %s", name)
|
||||
logger.debug("Failed to set value of %s for %s(%s)", dest, talker.name, talker.id)
|
||||
|
||||
|
||||
def form_settings_to_config(sources: dict[str, QtWidgets.QWidget], config: settngs.Config[ct_ns]) -> None:
|
||||
# Source combo box value
|
||||
config[0].talker_source = sources["cbx_select_talker"].currentData()
|
||||
def form_settings_to_config(sources: Sources, config: settngs.Config) -> settngs.Config[ct_ns]:
|
||||
# Update the currently selected talker
|
||||
config.values.Sources_source = sources.cbx_sources.currentData()
|
||||
cfg = settngs.normalize_config(config, True, True)
|
||||
|
||||
for tab in sources["tabs"].items():
|
||||
for name, widget in tab[1].widgets.items():
|
||||
# Iterate over the tabs, the talker is included in the tab so no extra lookup is needed
|
||||
for talker, tab in sources.tabs:
|
||||
talker_options = cfg.values[group_for_plugin(talker)]
|
||||
# dest is guaranteed to be unique within a talker and refer to the correct item in config.values['group name']
|
||||
for dest, widget in tab.widgets.items():
|
||||
widget_value = None
|
||||
if isinstance(widget, (QtWidgets.QSpinBox, QtWidgets.QDoubleSpinBox)):
|
||||
widget_value = widget.value()
|
||||
@ -204,83 +217,88 @@ def form_settings_to_config(sources: dict[str, QtWidgets.QWidget], config: settn
|
||||
elif isinstance(widget, QtWidgets.QCheckBox):
|
||||
widget_value = widget.isChecked()
|
||||
|
||||
setattr(config[0], name, widget_value)
|
||||
talker_options[dest] = widget_value
|
||||
return cast(settngs.Config[ct_ns], settngs.get_namespace(cfg, True, True))
|
||||
|
||||
|
||||
def generate_source_option_tabs(
|
||||
comic_talker_tab: QtWidgets.QWidget,
|
||||
config: settngs.Config[ct_ns],
|
||||
talkers: dict[str, ComicTalker],
|
||||
) -> dict[str, QtWidgets.QWidget]:
|
||||
) -> Sources:
|
||||
"""
|
||||
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()
|
||||
|
||||
# Store all widgets as to allow easier access to their values vs. using findChildren etc. on the tab widget
|
||||
sources: Sources = Sources(QtWidgets.QComboBox(), [])
|
||||
|
||||
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(sources[0], 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():
|
||||
for t_id, talker in talkers.items():
|
||||
# Add source to general tab dropdown list
|
||||
cbx_select_talker.addItem(talker_obj.name, talker_id)
|
||||
sources.cbx_sources.addItem(talker.name, t_id)
|
||||
tab = TalkerTab(tab=QtWidgets.QWidget(), widgets={})
|
||||
|
||||
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():
|
||||
url_option: settngs.Setting | None = None
|
||||
key_option: settngs.Setting | None = None
|
||||
for option in config.definitions[group_for_plugin(talker)].v.values():
|
||||
if not option.file:
|
||||
continue
|
||||
if option.dest in (f"{talker_id}_url", f"{talker_id}_key"):
|
||||
continue
|
||||
current_widget = None
|
||||
if option._guess_type() is bool:
|
||||
elif option.dest == f"{t_id}_key":
|
||||
key_option = option
|
||||
elif option.dest == f"{t_id}_url":
|
||||
url_option = option
|
||||
elif option._guess_type() is bool:
|
||||
current_widget = generate_checkbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
tab.widgets[option.dest] = current_widget
|
||||
elif option._guess_type() is int:
|
||||
current_widget = generate_spinbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
tab.widgets[option.dest] = current_widget
|
||||
elif option._guess_type() is float:
|
||||
current_widget = generate_doublespinbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
tab.widgets[option.dest] = current_widget
|
||||
|
||||
elif option._guess_type() is str:
|
||||
current_widget = generate_textbox(option, layout_grid)
|
||||
sources["tabs"][tab_name].widgets[option.internal_name] = current_widget
|
||||
tab.widgets[option.dest] = current_widget
|
||||
else:
|
||||
logger.debug(f"Unsupported talker option found. Name: {option.internal_name} Type: {option.type}")
|
||||
|
||||
# The key and url options are always defined.
|
||||
# If they aren't something has gone wrong with the talker, remove it
|
||||
if key_option is None or url_option is None:
|
||||
del talkers[t_id]
|
||||
continue
|
||||
|
||||
# Add talker URL and API key fields
|
||||
generate_api_widgets(talker_id, sources, config, layout_grid, talkers)
|
||||
generate_api_widgets(talker, tab, key_option, url_option, layout_grid)
|
||||
|
||||
# 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)
|
||||
tab.tab.setLayout(layout_grid)
|
||||
|
||||
# Add new sub tab to Comic Source tab
|
||||
talker_tabs.addTab(sources["tabs"][tab_name].tab, talker_obj.name)
|
||||
talker_tabs.addTab(tab.tab, talker.name)
|
||||
sources.tabs.append((talker, tab))
|
||||
|
||||
return sources
|
||||
|
@ -44,7 +44,7 @@ install_requires =
|
||||
pyrate-limiter>=2.6,<3
|
||||
rapidfuzz>=2.12.0
|
||||
requests==2.*
|
||||
settngs==0.7.1
|
||||
settngs==0.7.2
|
||||
text2digits
|
||||
typing-extensions>=4.3.0
|
||||
wordninja
|
||||
|
@ -10,8 +10,8 @@ from testing.comicdata import search_results
|
||||
|
||||
def test_create_cache(config, mock_version):
|
||||
config, definitions = config
|
||||
comictalker.comiccacher.ComicCacher(config.runtime_config.user_cache_dir, mock_version[0])
|
||||
assert config.runtime_config.user_cache_dir.exists()
|
||||
comictalker.comiccacher.ComicCacher(config.Runtime_Options_config.user_cache_dir, mock_version[0])
|
||||
assert config.Runtime_Options_config.user_cache_dir.exists()
|
||||
|
||||
|
||||
def test_search_results(comic_cache):
|
||||
|
@ -117,7 +117,7 @@ def comicvine_api(monkeypatch, cbz, comic_cache, mock_version, config) -> comict
|
||||
|
||||
cv = comictalker.talkers.comicvine.ComicVineTalker(
|
||||
version=mock_version[0],
|
||||
cache_folder=config[0].runtime_config.user_cache_dir,
|
||||
cache_folder=config[0].Runtime_Options_config.user_cache_dir,
|
||||
)
|
||||
manager = settngs.Manager()
|
||||
manager.add_persistent_group("comicvine", cv.register_settings)
|
||||
@ -174,14 +174,14 @@ def config(tmp_path):
|
||||
app.register_settings()
|
||||
|
||||
defaults = app.parse_settings(comictaggerlib.ctsettings.ComicTaggerPaths(tmp_path / "config"), "")
|
||||
defaults[0].runtime_config.user_data_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].runtime_config.user_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].runtime_config.user_cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].runtime_config.user_state_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].runtime_config.user_log_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].Runtime_Options_config.user_data_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].Runtime_Options_config.user_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].Runtime_Options_config.user_cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].Runtime_Options_config.user_state_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults[0].Runtime_Options_config.user_log_dir.mkdir(parents=True, exist_ok=True)
|
||||
yield defaults
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def comic_cache(config, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
|
||||
yield comictalker.comiccacher.ComicCacher(config[0].runtime_config.user_cache_dir, mock_version[0])
|
||||
yield comictalker.comiccacher.ComicCacher(config[0].Runtime_Options_config.user_cache_dir, mock_version[0])
|
||||
|
Loading…
Reference in New Issue
Block a user