Update to settngs 0.3.0

Use the namespace instead of a dictionary
Cleanup setting names
This commit is contained in:
Timmy Welch 2022-12-15 20:10:35 -08:00
parent e5c3692bb9
commit 440479da8c
No known key found for this signature in database
20 changed files with 406 additions and 439 deletions

View File

@ -42,7 +42,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
match_set_list: list[MultipleMatch],
style: int,
fetch_func: Callable[[IssueResult], GenericMetadata],
options: settngs.ConfigValues,
options: settngs.Namespace,
talker_api: ComicTalker,
) -> None:
super().__init__(parent)
@ -54,10 +54,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.current_match_set: MultipleMatch = match_set_list[0]
self.altCoverWidget = CoverImageWidget(
self.altCoverContainer,
CoverImageWidget.AltCoverMode,
options["runtime"]["config"].user_cache_dir,
talker_api,
self.altCoverContainer, CoverImageWidget.AltCoverMode, options.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
gridlayout.addWidget(self.altCoverWidget)
@ -245,10 +242,10 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
md = ca.read_metadata(self._style)
if md.is_empty:
md = ca.metadata_from_filename(
self.options["filename"]["complicated_parser"],
self.options["filename"]["remove_c2c"],
self.options["filename"]["remove_fcbd"],
self.options["filename"]["remove_publisher"],
self.options.filename_complicated_parser,
self.options.filename_remove_c2c,
self.options.filename_remove_fcbd,
self.options.filename_remove_publisher,
)
# now get the particular issue data

View File

@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
class AutoTagStartWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, options: settngs.ConfigValues, msg: str) -> None:
def __init__(self, parent: QtWidgets.QWidget, options: settngs.Namespace, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "autotagstartwindow.ui", self)
@ -40,16 +40,16 @@ class AutoTagStartWindow(QtWidgets.QDialog):
self.cbxSpecifySearchString.setChecked(False)
self.cbxSplitWords.setChecked(False)
self.sbNameMatchSearchThresh.setValue(self.options["identifier"]["series_match_identify_thresh"])
self.sbNameMatchSearchThresh.setValue(self.options.identifier_series_match_identify_thresh)
self.leSearchString.setEnabled(False)
self.cbxSaveOnLowConfidence.setChecked(self.options["autotag"]["save_on_low_confidence"])
self.cbxDontUseYear.setChecked(self.options["autotag"]["dont_use_year_when_identifying"])
self.cbxAssumeIssueOne.setChecked(self.options["autotag"]["assume_1_if_no_issue_num"])
self.cbxIgnoreLeadingDigitsInFilename.setChecked(self.options["autotag"]["ignore_leading_numbers_in_filename"])
self.cbxRemoveAfterSuccess.setChecked(self.options["autotag"]["remove_archive_after_successful_match"])
self.cbxWaitForRateLimit.setChecked(self.options["autotag"]["wait_and_retry_on_rate_limit"])
self.cbxAutoImprint.setChecked(self.options["comicvine"]["auto_imprint"])
self.cbxSaveOnLowConfidence.setChecked(self.options.autotag_save_on_low_confidence)
self.cbxDontUseYear.setChecked(self.options.autotag_dont_use_year_when_identifying)
self.cbxAssumeIssueOne.setChecked(self.options.autotag_assume_1_if_no_issue_num)
self.cbxIgnoreLeadingDigitsInFilename.setChecked(self.options.autotag_ignore_leading_numbers_in_filename)
self.cbxRemoveAfterSuccess.setChecked(self.options.autotag_remove_archive_after_successful_match)
self.cbxWaitForRateLimit.setChecked(self.options.autotag_wait_and_retry_on_rate_limit)
self.cbxAutoImprint.setChecked(self.options.comicvine_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
@ -75,7 +75,7 @@ class AutoTagStartWindow(QtWidgets.QDialog):
self.remove_after_success = False
self.wait_and_retry_on_rate_limit = False
self.search_string = ""
self.name_length_match_tolerance = self.options["comicvine"]["series_match_search_thresh"]
self.name_length_match_tolerance = self.options.comicvine_series_match_search_thresh
self.split_words = self.cbxSplitWords.isChecked()
def search_string_toggle(self) -> None:
@ -95,12 +95,12 @@ class AutoTagStartWindow(QtWidgets.QDialog):
self.split_words = self.cbxSplitWords.isChecked()
# persist some settings
self.options["autotag"]["save_on_low_confidence"] = self.auto_save_on_low
self.options["autotag"]["dont_use_year_when_identifying"] = self.dont_use_year
self.options["autotag"]["assume_1_if_no_issue_num"] = self.assume_issue_one
self.options["autotag"]["ignore_leading_numbers_in_filename"] = self.ignore_leading_digits_in_filename
self.options["autotag"]["remove_archive_after_successful_match"] = self.remove_after_success
self.options["autotag"]["wait_and_retry_on_rate_limit"] = self.wait_and_retry_on_rate_limit
self.options.autotag_save_on_low_confidence = self.auto_save_on_low
self.options.autotag_dont_use_year_when_identifying = self.dont_use_year
self.options.autotag_assume_1_if_no_issue_num = self.assume_issue_one
self.options.autotag_ignore_leading_numbers_in_filename = self.ignore_leading_digits_in_filename
self.options.autotag_remove_archive_after_successful_match = self.remove_after_success
self.options.autotag_wait_and_retry_on_rate_limit = self.wait_and_retry_on_rate_limit
if self.cbxSpecifySearchString.isChecked():
self.search_string = self.leSearchString.text()

View File

@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
class CBLTransformer:
def __init__(self, metadata: GenericMetadata, options: settngs.ConfigValues) -> None:
def __init__(self, metadata: GenericMetadata, options: settngs.Namespace) -> None:
self.metadata = metadata
self.options = options
@ -41,7 +41,7 @@ class CBLTransformer:
for item in items:
append_to_tags_if_unique(item)
if self.options["cbl"]["assume_lone_credit_is_primary"]:
if self.options.cbl_assume_lone_credit_is_primary:
# helper
def set_lone_primary(role_list: list[str]) -> tuple[CreditMetadata | None, int]:
@ -68,19 +68,19 @@ class CBLTransformer:
c["primary"] = False
self.metadata.add_credit(c["person"], "Artist", True)
if self.options["cbl"]["copy_characters_to_tags"]:
if self.options.cbl_copy_characters_to_tags:
add_string_list_to_tags(self.metadata.characters)
if self.options["cbl"]["copy_teams_to_tags"]:
if self.options.cbl_copy_teams_to_tags:
add_string_list_to_tags(self.metadata.teams)
if self.options["cbl"]["copy_locations_to_tags"]:
if self.options.cbl_copy_locations_to_tags:
add_string_list_to_tags(self.metadata.locations)
if self.options["cbl"]["copy_storyarcs_to_tags"]:
if self.options.cbl_copy_storyarcs_to_tags:
add_string_list_to_tags(self.metadata.story_arc)
if self.options["cbl"]["copy_notes_to_comments"]:
if self.options.cbl_copy_notes_to_comments:
if self.metadata.notes is not None:
if self.metadata.comments is None:
self.metadata.comments = ""
@ -89,7 +89,7 @@ class CBLTransformer:
if self.metadata.notes not in self.metadata.comments:
self.metadata.comments += self.metadata.notes
if self.options["cbl"]["copy_weblink_to_comments"]:
if self.options.cbl_copy_weblink_to_comments:
if self.metadata.web_link is not None:
if self.metadata.comments is None:
self.metadata.comments = ""

View File

@ -53,14 +53,14 @@ class CLI:
logger.exception(f"Error retrieving issue details. Save aborted.\n{e}")
return GenericMetadata()
if self.options["cbl"]["apply_cbl_transform_on_cv_import"]:
if self.options.cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options).apply()
return ct_md
def actual_metadata_save(self, ca: ComicArchive, md: GenericMetadata) -> bool:
if not self.options["runtime"]["dryrun"]:
for metadata_style in self.options["runtime"]["type"]:
if not self.options.runtime_dryrun:
for metadata_style in self.options.runtime_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])
@ -69,7 +69,7 @@ class CLI:
print("Save complete.")
logger.info("Save complete.")
else:
if self.options["runtime"]["terse"]:
if self.options.runtime_quiet:
logger.info("dry-run option was set, so nothing was written")
print("dry-run option was set, so nothing was written")
else:
@ -97,7 +97,7 @@ class CLI:
m["issue_title"],
)
)
if self.options["runtime"]["interactive"]:
if self.options.runtime_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":
@ -108,7 +108,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.options["comicvine"]["clear_metadata_on_import"]:
if self.options.comicvine_clear_metadata_on_import:
md = ct_md
else:
notes = (
@ -117,14 +117,14 @@ class CLI:
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.options["comicvine"]["auto_imprint"]:
if self.options.comicvine_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.options["runtime"]["show_save_summary"]:
if self.options.runtime_summary:
if len(match_results.good_matches) > 0:
print("\nSuccessful matches:\n------------------")
for f in match_results.good_matches:
@ -145,7 +145,7 @@ class CLI:
for f in match_results.fetch_data_failures:
print(f)
if not self.options["runtime"]["show_save_summary"] and not self.options["runtime"]["interactive"]:
if not self.options.runtime_summary and not self.options.runtime_interactive:
# just quit if we're not interactive or showing the summary
return
@ -165,14 +165,14 @@ class CLI:
self.display_match_set_for_choice(label, match_set)
def run(self) -> None:
if len(self.options["runtime"]["file_list"]) < 1:
if len(self.options.runtime_file_list) < 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.options["runtime"]["file_list"]) > 1
self.batch_mode = len(self.options.runtime_file_list) > 1
for f in self.options["runtime"]["file_list"]:
for f in self.options.runtime_file_list:
self.process_file_cli(f, match_results)
sys.stdout.flush()
@ -183,18 +183,18 @@ class CLI:
md.set_default_page_list(ca.get_number_of_pages())
# now, overlay the parsed filename info
if self.options["runtime"]["parse_filename"]:
if self.options.runtime_parse_filename:
f_md = ca.metadata_from_filename(
self.options["filename"]["complicated_parser"],
self.options["filename"]["remove_c2c"],
self.options["filename"]["remove_fcbd"],
self.options["filename"]["remove_publisher"],
self.options["runtime"]["split_words"],
self.options.filename_complicated_parser,
self.options.filename_remove_c2c,
self.options.filename_remove_fcbd,
self.options.filename_remove_publisher,
self.options.runtime_split_words,
)
md.overlay(f_md)
for metadata_style in self.options["runtime"]["type"]:
for metadata_style in self.options.runtime_type:
if ca.has_metadata(metadata_style):
try:
t_md = ca.read_metadata(metadata_style)
@ -204,12 +204,12 @@ class CLI:
logger.error("Failed to load metadata for %s: %s", ca.path, e)
# finally, use explicit stuff
md.overlay(self.options["runtime"]["metadata"])
md.overlay(self.options.runtime_metadata)
return md
def print(self, ca: ComicArchive) -> None:
if not self.options["runtime"]["type"]:
if not self.options.runtime_type:
page_count = ca.get_number_of_pages()
brief = ""
@ -246,38 +246,38 @@ class CLI:
print(brief)
if self.options["runtime"]["terse"]:
if self.options.runtime_quiet:
return
print()
if not self.options["runtime"]["type"] or MetaDataStyle.CIX in self.options["runtime"]["type"]:
if not self.options.runtime_type or MetaDataStyle.CIX in self.options.runtime_type:
if ca.has_metadata(MetaDataStyle.CIX):
print("--------- ComicRack tags ---------")
try:
if self.options["runtime"]["raw"]:
if self.options.runtime_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.options["runtime"]["type"] or MetaDataStyle.CBI in self.options["runtime"]["type"]:
if not self.options.runtime_type or MetaDataStyle.CBI in self.options.runtime_type:
if ca.has_metadata(MetaDataStyle.CBI):
print("------- ComicBookLover tags -------")
try:
if self.options["runtime"]["raw"]:
if self.options.runtime_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.options["runtime"]["type"] or MetaDataStyle.COMET in self.options["runtime"]["type"]:
if not self.options.runtime_type or MetaDataStyle.COMET in self.options.runtime_type:
if ca.has_metadata(MetaDataStyle.COMET):
print("----------- CoMet tags -----------")
try:
if self.options["runtime"]["raw"]:
if self.options.runtime_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.options["runtime"]["type"]:
for metadata_style in self.options.runtime_type:
style_name = MetaDataStyle.name[metadata_style]
if ca.has_metadata(metadata_style):
if not self.options["runtime"]["dryrun"]:
if not self.options.runtime_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.options["runtime"]["type"]:
for metadata_style in self.options.runtime_type:
dst_style_name = MetaDataStyle.name[metadata_style]
if self.options["runtime"]["no_overwrite"] and ca.has_metadata(metadata_style):
if not self.options.runtime_overwrite and ca.has_metadata(metadata_style):
print(f"{ca.path}: Already has {dst_style_name} tags. Not overwriting.")
return
if self.options["commands"]["copy"] == metadata_style:
if self.options.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.options["commands"]["copy"]]
if ca.has_metadata(self.options["commands"]["copy"]):
if not self.options["runtime"]["dryrun"]:
src_style_name = MetaDataStyle.name[self.options.commands_copy]
if ca.has_metadata(self.options.commands_copy):
if not self.options.runtime_dryrun:
try:
md = ca.read_metadata(self.options["commands"]["copy"])
md = ca.read_metadata(self.options.commands_copy)
except Exception as e:
md = GenericMetadata()
logger.error("Failed to load metadata for %s: %s", ca.path, e)
if self.options["apply_cbl_transform_on_bulk_operation"] and metadata_style == MetaDataStyle.CBI:
if self.options.apply_transform_on_bulk_operation_ndetadata_style == MetaDataStyle.CBI:
md = CBLTransformer(md, self.options).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 self.options["runtime"]["no_overwrite"]:
for metadata_style in self.options["runtime"]["type"]:
if not self.options.runtime_overwrite:
for metadata_style in self.options.runtime_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.options["runtime"]["assume_issue_one"]:
if self.options.runtime_assume_issue_one:
md.issue = "1"
# now, search online
if self.options["runtime"]["online"]:
if self.options["runtime"]["issue_id"] is not None:
if self.options.runtime_online:
if self.options.runtime_issue_id is not None:
# we were given the actual issue ID to search with
try:
ct_md = self.talker_api.fetch_comic_data(self.options["runtime"]["issue_id"])
ct_md = self.talker_api.fetch_comic_data(self.options.runtime_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.options["runtime"]["issue_id"])
logger.error("No match for ID %s was found.", self.options.runtime_issue_id)
match_results.no_matches.append(str(ca.path.absolute()))
return
if self.options["cbl"]["apply_cbl_transform_on_cv_import"]:
if self.options.cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options).apply()
else:
if md is None or md.is_empty:
@ -371,7 +371,7 @@ class CLI:
ii = IssueIdentifier(ca, self.options, self.talker_api)
def myoutput(text: str) -> None:
if self.options["runtime"]["verbose"]:
if self.options.runtime_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.options["runtime"]["abort_on_low_confidence"]:
if low_confidence and self.options.runtime_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.options["comicvine"]["clear_metadata_on_import"]:
if self.options.comicvine_clear_metadata_on_import:
md = ct_md
else:
notes = (
@ -437,7 +437,7 @@ class CLI:
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.options["comicvine"]["auto_imprint"]:
if self.options.comicvine_auto_imprint:
md.fix_publisher()
# ok, done building our metadata. time to save
@ -459,7 +459,7 @@ class CLI:
return
new_ext = "" # default
if self.options["filename"]["rename_set_extension_based_on_archive"]:
if self.options.filename_rename_set_extension_based_on_archive:
if ca.is_sevenzip():
new_ext = ".cb7"
elif ca.is_zip():
@ -469,13 +469,13 @@ class CLI:
renamer = FileRenamer(
md,
platform="universal" if self.options["filename"]["rename_strict"] else "auto",
replacements=self.options["rename"]["replacements"],
platform="universal" if self.options.filename_rename_strict else "auto",
replacements=self.options.rename_replacements,
)
renamer.set_template(self.options["filename"]["rename_template"])
renamer.set_issue_zero_padding(self.options["filename"]["rename_issue_number_padding"])
renamer.set_smart_cleanup(self.options["filename"]["rename_use_smart_string_cleanup"])
renamer.move = self.options["filename"]["rename_move_to_dir"]
renamer.set_template(self.options.filename_rename_template)
renamer.set_issue_zero_padding(self.options.filename_rename_issue_number_padding)
renamer.set_smart_cleanup(self.options.filename_rename_use_smart_string_cleanup)
renamer.move = self.options.filename_rename_move_to_dir
try:
new_name = renamer.determine_name(ext=new_ext)
@ -487,16 +487,16 @@ 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.options["filename"]["rename_template"],
self.options.filename_rename_template,
)
return
except Exception:
logger.exception(
"Formatter failure: %s metadata: %s", self.options["filename"]["rename_template"], renamer.metadata
"Formatter failure: %s metadata: %s", self.options.filename_rename_template, renamer.metadata
)
folder = get_rename_dir(
ca, self.options["filename"]["rename_dir"] if self.options["filename"]["rename_move_to_dir"] else None
ca, self.options.filename_rename_dir if self.options.filename_rename_move_to_dir else None
)
full_path = folder / new_name
@ -506,7 +506,7 @@ class CLI:
return
suffix = ""
if not self.options["runtime"]["dryrun"]:
if not self.options.runtime_dryrun:
# rename the file
try:
ca.rename(utils.unique_file(full_path))
@ -529,7 +529,7 @@ class CLI:
filename_path = ca.path
new_file = filename_path.with_suffix(".cbz")
if self.options["runtime"]["abort_on_conflict"] and new_file.exists():
if self.options.runtime_abort_on_conflict and new_file.exists():
print(msg_hdr + f"{new_file.name} already exists in the that folder.")
return
@ -537,10 +537,10 @@ class CLI:
delete_success = False
export_success = False
if not self.options["runtime"]["dryrun"]:
if not self.options.runtime_dryrun:
if ca.export_as_zip(new_file):
export_success = True
if self.options["runtime"]["delete_after_zip_export"]:
if self.options.runtime_delete_after_zip_export:
try:
filename_path.unlink(missing_ok=True)
delete_success = True
@ -552,7 +552,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.options["runtime"]["delete_after_zip_export"]:
if self.options.runtime_delete_after_zip_export:
msg += " and delete original."
print(msg)
return
@ -560,7 +560,7 @@ class CLI:
msg = msg_hdr
if export_success:
msg += f"Archive exported successfully to: {os.path.split(new_file)[1]}"
if self.options["runtime"]["delete_after_zip_export"] and delete_success:
if self.options.runtime_delete_after_zip_export and delete_success:
msg += " (Original deleted) "
else:
msg += "Archive failed to export!"
@ -572,35 +572,35 @@ class CLI:
logger.error("Cannot find %s", filename)
return
ca = ComicArchive(filename, self.options["general"]["rar_exe_path"], str(graphics_path / "nocover.png"))
ca = ComicArchive(filename, self.options.general_rar_exe_path, str(graphics_path / "nocover.png"))
if not ca.seems_to_be_a_comic_archive():
logger.error("Sorry, but %s is not a comic archive!", filename)
return
if not ca.is_writable() and (
self.options["commands"]["delete"]
or self.options["commands"]["copy"]
or self.options["commands"]["save"]
or self.options["commands"]["rename"]
self.options.commands_delete
or self.options.commands_copy
or self.options.commands_save
or self.options.commands_rename
):
logger.error("This archive is not writable")
return
if self.options["commands"]["print"]:
if self.options.commands_print:
self.print(ca)
elif self.options["commands"]["delete"]:
elif self.options.commands_delete:
self.delete(ca)
elif self.options["commands"]["copy"] is not None:
elif self.options.commands_copy is not None:
self.copy(ca)
elif self.options["commands"]["save"]:
elif self.options.commands_save:
self.save(ca, match_results)
elif self.options["commands"]["rename"]:
elif self.options.commands_rename:
self.rename(ca)
elif self.options["commands"]["export_to_zip"]:
elif self.options.commands_export_to_zip:
self.export(ca)

View File

@ -116,17 +116,18 @@ def register_options(parser: settngs.Manager) -> None:
file=False,
)
parser.add_setting(
"--noabort",
"--abort",
dest="abort_on_low_confidence",
action="store_false",
help="""Don't abort save operation when online match\nis of low confidence.\n\n""",
action=argparse.BooleanOptionalAction,
default=True,
help="""Abort save operation when online match\nis of low confidence.\n\n""",
file=False,
)
parser.add_setting(
"--nosummary",
dest="show_save_summary",
action="store_false",
help="Suppress the default summary after a save operation.\n\n",
"--summary",
default=True,
action=argparse.BooleanOptionalAction,
help="Show the summary after a save operation.\n\n",
file=False,
)
parser.add_setting(
@ -175,7 +176,8 @@ def register_options(parser: settngs.Manager) -> None:
file=False,
)
parser.add_setting(
"--terse",
"--quiet",
"-q",
action="store_true",
help="Don't say much (for print mode).",
file=False,
@ -191,10 +193,11 @@ def register_options(parser: settngs.Manager) -> None:
file=False,
)
parser.add_setting(
"--no-overwrite",
dest="no_overwrite",
action="store_true",
help="""Don't modify tag block if it already exists (relevant for -s or -c).""",
"--overwrite",
dest="overwrite",
action=argparse.BooleanOptionalAction,
default=True,
help="""Apply metadata to already tagged archives (relevant for -s or -c).""",
file=False,
)
parser.add_setting("files", nargs="*", file=False)
@ -264,78 +267,78 @@ def register_commandline(parser: settngs.Manager) -> None:
parser.add_group("runtime", register_options)
def validate_commandline_options(options: settngs.Values, parser: settngs.Manager) -> settngs.Values:
def validate_commandline_options(options: settngs.Config[settngs.Values], parser: settngs.Manager) -> settngs.Values:
if options["commands"]["version"]:
if options[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",
)
options["runtime"]["no_gui"] = any(
options[0].runtime_no_gui = any(
[
options["commands"]["print"],
options["commands"]["delete"],
options["commands"]["save"],
options["commands"]["copy"],
options["commands"]["rename"],
options["commands"]["export_to_zip"],
options["commands"]["only_set_cv_key"],
options[0].commands_print,
options[0].commands_delete,
options[0].commands_save,
options[0].commands_copy,
options[0].commands_rename,
options[0].commands_export_to_zip,
options[0].commands_only_set_cv_key,
]
)
if platform.system() == "Windows" and options["runtime"]["glob"]:
if platform.system() == "Windows" and options[0].runtime_glob:
# no globbing on windows shell, so do it for them
import glob
globs = options["runtime"]["files"]
options["runtime"]["files"] = []
globs = options[0].runtime_files
options[0].runtime_files = []
for item in globs:
options["runtime"]["files"].extend(glob.glob(item))
options[0].runtime_files.extend(glob.glob(item))
if (
options["commands"]["only_set_cv_key"]
and options["comicvine"]["cv_api_key"] is None
and options["comicvine"]["cv_url"] is None
options[0].commands_only_set_cv_key
and options[0].comicvine_cv_api_key is None
and options[0].comicvine_cv_url is None
):
parser.exit(message="Key not given!\n", status=1)
if not options["commands"]["only_set_cv_key"] and options["runtime"]["no_gui"] and not options["runtime"]["files"]:
if not options[0].commands_only_set_cv_key and options[0].runtime_no_gui and not options[0].runtime_files:
parser.exit(message="Command requires at least one filename!\n", status=1)
if options["commands"]["delete"] and not options["runtime"]["type"]:
if options[0].commands_delete and not options[0].runtime_type:
parser.exit(message="Please specify the type to delete with -t\n", status=1)
if options["commands"]["save"] and not options["runtime"]["type"]:
if options[0].commands_save and not options[0].runtime_type:
parser.exit(message="Please specify the type to save with -t\n", status=1)
if options["commands"]["copy"]:
if not options["runtime"]["type"]:
if options[0].commands_copy:
if not options[0].runtime_type:
parser.exit(message="Please specify the type to copy to with -t\n", status=1)
if len(options["commands"]["copy"]) > 1:
if len(options[0].commands_copy) > 1:
parser.exit(message="Please specify only one type to copy to with -c\n", status=1)
options["commands"]["copy"] = options["commands"]["copy"][0]
options[0].commands_copy = options[0].commands_copy[0]
if options["runtime"]["recursive"]:
options["runtime"]["file_list"] = utils.get_recursive_filelist(options["runtime"]["files"])
if options[0].runtime_recursive:
options[0].runtime_file_list = utils.get_recursive_filelist(options[0].runtime_files)
else:
options["runtime"]["file_list"] = options["runtime"]["files"]
options[0].runtime_file_list = options[0].runtime_files
# take a crack at finding rar exe, if not set already
if options["general"]["rar_exe_path"].strip() in ("", "rar"):
if options[0].general_rar_exe_path.strip() in ("", "rar"):
if platform.system() == "Windows":
# look in some likely places for Windows machines
if os.path.exists(r"C:\Program Files\WinRAR\Rar.exe"):
options["general"]["rar_exe_path"] = r"C:\Program Files\WinRAR\Rar.exe"
options[0].general_rar_exe_path = r"C:\Program Files\WinRAR\Rar.exe"
elif os.path.exists(r"C:\Program Files (x86)\WinRAR\Rar.exe"):
options["general"]["rar_exe_path"] = r"C:\Program Files (x86)\WinRAR\Rar.exe"
options[0].general_rar_exe_path = r"C:\Program Files (x86)\WinRAR\Rar.exe"
else:
if os.path.exists("/opt/homebrew/bin"):
utils.add_to_path("/opt/homebrew/bin")
# see if it's in the path of unix user
rarpath = utils.which("rar")
if rarpath is not None:
options["general"]["rar_exe_path"] = "rar"
options[0].general_rar_exe_path = "rar"
return options

View File

@ -26,17 +26,17 @@ def general(parser: settngs.Manager) -> None:
def internal(parser: settngs.Manager) -> None:
# automatic settings
parser.add_setting("install_id", default=uuid.uuid4().hex, cmdline=False)
parser.add_setting("last_selected_save_data_style", default=0, cmdline=False)
parser.add_setting("last_selected_load_data_style", default=0, cmdline=False)
parser.add_setting("save_data_style", default=0, cmdline=False)
parser.add_setting("load_data_style", default=0, cmdline=False)
parser.add_setting("last_opened_folder", default="", cmdline=False)
parser.add_setting("last_main_window_width", default=0, cmdline=False)
parser.add_setting("last_main_window_height", default=0, cmdline=False)
parser.add_setting("last_main_window_x", default=0, cmdline=False)
parser.add_setting("last_main_window_y", default=0, cmdline=False)
parser.add_setting("last_form_side_width", default=-1, cmdline=False)
parser.add_setting("last_list_side_width", default=-1, cmdline=False)
parser.add_setting("last_filelist_sorted_column", default=-1, cmdline=False)
parser.add_setting("last_filelist_sorted_order", default=0, cmdline=False)
parser.add_setting("window_width", default=0, cmdline=False)
parser.add_setting("window_height", default=0, cmdline=False)
parser.add_setting("window_x", default=0, cmdline=False)
parser.add_setting("window_y", default=0, cmdline=False)
parser.add_setting("form_width", default=-1, cmdline=False)
parser.add_setting("list_width", default=-1, cmdline=False)
parser.add_setting("sort_column", default=-1, cmdline=False)
parser.add_setting("sort_direction", default=0, cmdline=False)
def identifier(parser: settngs.Manager) -> None:
@ -155,8 +155,8 @@ def cbl(parser: settngs.Manager) -> None:
parser.add_setting("--copy-storyarcs-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-notes-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-weblink-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-cbl-transform-on-cv-import", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-cbl-transform-on-bulk-operation", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-transform-on-import", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-transform-on-bulk-operation", default=False, action=argparse.BooleanOptionalAction)
def rename(parser: settngs.Manager) -> None:
@ -175,14 +175,16 @@ def rename(parser: settngs.Manager) -> None:
help="Attempts to intelligently cleanup whitespace when renaming",
)
parser.add_setting(
"--set-extension-based-on-archive",
"--auto-extension",
dest="set_extension_based_on_archive",
default=True,
action=argparse.BooleanOptionalAction,
help="Automatically sets the extension based on the archive type e.g. cbr for rar, cbz for zip",
)
parser.add_setting("--dir", default="", help="The directory to move renamed files to")
parser.add_setting(
"--move-to-dir",
"--move",
dest="move_to_dir",
default=False,
action=argparse.BooleanOptionalAction,
help="Enables moving renamed files to a separate directory",
@ -231,7 +233,7 @@ def autotag(parser: settngs.Manager) -> None:
parser.add_setting("remove_archive_after_successful_match", default=False, cmdline=False)
parser.add_setting(
"-w",
"--wait-on-cv-rate-limit",
"--wait-on-rate-limit",
dest="wait_and_retry_on_rate_limit",
action=argparse.BooleanOptionalAction,
default=True,
@ -239,13 +241,11 @@ def autotag(parser: settngs.Manager) -> None:
)
def validate_settings(options: dict[str, dict[str, Any]], parser: settngs.Manager) -> dict[str, dict[str, Any]]:
options["identifier"]["publisher_filter"] = [
x.strip() for x in options["identifier"]["publisher_filter"] if x.strip()
]
options["rename"]["replacements"] = Replacements(
[Replacement(x[0], x[1], x[2]) for x in options["rename"]["replacements"][0]],
[Replacement(x[0], x[1], x[2]) for x in options["rename"]["replacements"][1]],
def validate_settings(options: settngs.Config[settngs.Values], parser: settngs.Manager) -> dict[str, dict[str, Any]]:
options[0].identifier_publisher_filter = [x.strip() for x in options[0].identifier_publisher_filter if x.strip()]
options[0].rename_replacements = Replacements(
[Replacement(x[0], x[1], x[2]) for x in options[0].rename_replacements[0]],
[Replacement(x[0], x[1], x[2]) for x in options[0].rename_replacements[1]],
)
return options

View File

@ -57,10 +57,7 @@ class FileSelectionList(QtWidgets.QWidget):
dataColNum = fileColNum
def __init__(
self,
parent: QtWidgets.QWidget,
options: settngs.ConfigValues,
dirty_flag_verification: Callable[[str, str], bool],
self, parent: QtWidgets.QWidget, options: settngs.Namespace, dirty_flag_verification: Callable[[str, str], bool]
) -> None:
super().__init__(parent)
@ -227,7 +224,7 @@ class FileSelectionList(QtWidgets.QWidget):
else:
QtWidgets.QMessageBox.information(self, "File/Folder Open", "No readable comic archives were found.")
if rar_added and not utils.which(self.options["general"]["rar_exe_path"] or "rar"):
if rar_added and not utils.which(self.options.general_rar_exe_path or "rar"):
self.rar_ro_message()
self.twList.setSortingEnabled(True)
@ -281,7 +278,7 @@ class FileSelectionList(QtWidgets.QWidget):
if self.is_list_dupe(path):
return self.get_current_list_row(path)
ca = ComicArchive(path, self.options["general"]["rar_exe_path"], str(graphics_path / "nocover.png"))
ca = ComicArchive(path, self.options.general_rar_exe_path, str(graphics_path / "nocover.png"))
if ca.seems_to_be_a_comic_archive():
row: int = self.twList.rowCount()

View File

@ -82,10 +82,12 @@ except ImportError as e:
qt_available = False
def open_tagger_window(talker_api: ComicTalker, options: settngs.Config, error: tuple[str, bool] | None) -> None:
def open_tagger_window(
talker_api: ComicTalker, options: settngs.Config[settngs.Namespace], error: tuple[str, bool] | None
) -> None:
os.environ["QtWidgets.QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
args = []
if options[0]["runtime"]["darkmode"]:
if options[0].runtime_darkmode:
args.extend(["-platform", "windows:darkmode=2"])
args.extend(sys.argv)
app = Application(args)
@ -95,7 +97,7 @@ def open_tagger_window(talker_api: ComicTalker, options: settngs.Config, error:
raise SystemExit(1)
# needed to catch initial open file events (macOS)
app.openFileRequest.connect(lambda x: options[0]["runtime"]["files"].append(x.toLocalFile()))
app.openFileRequest.connect(lambda x: options[0].runtime_files.append(x.toLocalFile()))
if platform.system() == "Darwin":
# Set the MacOS dock icon
@ -123,7 +125,7 @@ def open_tagger_window(talker_api: ComicTalker, options: settngs.Config, error:
QtWidgets.QApplication.processEvents()
try:
tagger_window = TaggerWindow(options[0]["runtime"]["files"], options, talker_api)
tagger_window = TaggerWindow(options[0].runtime_files, options, talker_api)
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
tagger_window.show()

View File

@ -72,7 +72,7 @@ class IssueIdentifier:
result_one_good_match = 4
result_multiple_good_matches = 5
def __init__(self, comic_archive: ComicArchive, options: settngs.ConfigValues, talker_api: ComicTalker) -> None:
def __init__(self, comic_archive: ComicArchive, options: settngs.Namespace, talker_api: ComicTalker) -> None:
self.options = options
self.talker_api = talker_api
self.comic_archive: ComicArchive = comic_archive
@ -96,10 +96,10 @@ class IssueIdentifier:
# used to eliminate series names that are too long based on our search
# string
self.series_match_thresh = options["identifier"]["series_match_identify_thresh"]
self.series_match_thresh = options.identifier_series_match_identify_thresh
# used to eliminate unlikely publishers
self.publisher_filter = [s.strip().casefold() for s in options["identifier"]["publisher_filter"]]
self.publisher_filter = [s.strip().casefold() for s in options.identifier_publisher_filter]
self.additional_metadata = GenericMetadata()
self.output_function: Callable[[str], None] = IssueIdentifier.default_write_output
@ -201,10 +201,10 @@ class IssueIdentifier:
# try to get some metadata from filename
md_from_filename = ca.metadata_from_filename(
self.options["filename"]["complicated_parser"],
self.options["filename"]["remove_c2c"],
self.options["filename"]["remove_fcbd"],
self.options["filename"]["remove_publisher"],
self.options.filename_complicated_parser,
self.options.filename_remove_c2c,
self.options.filename_remove_fcbd,
self.options.filename_remove_publisher,
)
working_md = md_from_filename.copy()
@ -255,7 +255,7 @@ class IssueIdentifier:
return Score(score=0, url="", hash=0)
try:
url_image_data = ImageFetcher(self.options["runtime"]["config"].user_cache_dir).fetch(
url_image_data = ImageFetcher(self.options.runtime_config.user_cache_dir).fetch(
primary_thumb_url, blocking=True
)
except ImageFetcherException as e:
@ -277,7 +277,7 @@ class IssueIdentifier:
if use_remote_alternates:
for alt_url in alt_urls:
try:
alt_url_image_data = ImageFetcher(self.options["runtime"]["config"].user_cache_dir).fetch(
alt_url_image_data = ImageFetcher(self.options.runtime_config.user_cache_dir).fetch(
alt_url, blocking=True
)
except ImageFetcherException as e:

View File

@ -44,7 +44,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
def __init__(
self,
parent: QtWidgets.QWidget,
options: settngs.ConfigValues,
options: settngs.Namespace,
talker_api: ComicTalker,
series_id: int,
issue_number: str,
@ -54,10 +54,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
uic.loadUi(ui_path / "issueselectionwindow.ui", self)
self.coverWidget = CoverImageWidget(
self.coverImageContainer,
CoverImageWidget.AltCoverMode,
options["runtime"]["config"].user_cache_dir,
talker_api,
self.coverImageContainer, CoverImageWidget.AltCoverMode, options.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)
gridlayout.addWidget(self.coverWidget)

View File

@ -21,7 +21,6 @@ import logging.handlers
import platform
import signal
import sys
from typing import Any
import settngs
@ -50,8 +49,8 @@ logger = logging.getLogger("comictagger")
logger.setLevel(logging.DEBUG)
def update_publishers(options: dict[str, dict[str, Any]]) -> None:
json_file = options["runtime"]["config"].user_config_dir / "publishers.json"
def update_publishers(options: settngs.Namespace) -> None:
json_file = options.runtime_config.user_config_dir / "publishers.json"
if json_file.exists():
try:
utils.update_publishers(json.loads(json_file.read_text("utf-8")))
@ -91,23 +90,26 @@ class App:
ctoptions.register_settings(self.manager)
def parse_options(self, config_paths: ctoptions.ComicTaggerPaths) -> None:
config, self.config_load_success = self.manager.parse_config(config_paths.user_config_dir / "settings.json")
options, definitions = config
options = ctoptions.validate_commandline_options(options, self.manager)
options = ctoptions.validate_settings(options, self.manager)
self.options = settngs.Config(options, definitions)
self.options, self.config_load_success = self.manager.parse_config(
config_paths.user_config_dir / "settings.json"
)
self.options = self.manager.get_namespace(self.options)
self.options = ctoptions.validate_commandline_options(self.options, self.manager)
self.options = ctoptions.validate_settings(self.options, self.manager)
self.options = self.options
def initialize_dirs(self) -> None:
self.options[0]["runtime"]["config"].user_data_dir.mkdir(parents=True, exist_ok=True)
self.options[0]["runtime"]["config"].user_config_dir.mkdir(parents=True, exist_ok=True)
self.options[0]["runtime"]["config"].user_cache_dir.mkdir(parents=True, exist_ok=True)
self.options[0]["runtime"]["config"].user_state_dir.mkdir(parents=True, exist_ok=True)
self.options[0]["runtime"]["config"].user_log_dir.mkdir(parents=True, exist_ok=True)
logger.debug("user_data_dir: %s", self.options[0]["runtime"]["config"].user_data_dir)
logger.debug("user_config_dir: %s", self.options[0]["runtime"]["config"].user_config_dir)
logger.debug("user_cache_dir: %s", self.options[0]["runtime"]["config"].user_cache_dir)
logger.debug("user_state_dir: %s", self.options[0]["runtime"]["config"].user_state_dir)
logger.debug("user_log_dir: %s", self.options[0]["runtime"]["config"].user_log_dir)
self.options[0].runtime_config.user_data_dir.mkdir(parents=True, exist_ok=True)
self.options[0].runtime_config.user_config_dir.mkdir(parents=True, exist_ok=True)
self.options[0].runtime_config.user_cache_dir.mkdir(parents=True, exist_ok=True)
self.options[0].runtime_config.user_state_dir.mkdir(parents=True, exist_ok=True)
self.options[0].runtime_config.user_log_dir.mkdir(parents=True, exist_ok=True)
logger.debug("user_data_dir: %s", self.options[0].runtime_config.user_data_dir)
logger.debug("user_config_dir: %s", self.options[0].runtime_config.user_config_dir)
logger.debug("user_cache_dir: %s", self.options[0].runtime_config.user_cache_dir)
logger.debug("user_state_dir: %s", self.options[0].runtime_config.user_state_dir)
logger.debug("user_log_dir: %s", self.options[0].runtime_config.user_log_dir)
def main(self) -> None:
assert self.options is not None
@ -130,18 +132,18 @@ class App:
utils.load_publishers()
update_publishers(self.options[0])
if not qt_available and not self.options[0]["runtime"]["no_gui"]:
self.options[0]["runtime"]["no_gui"] = True
if not qt_available and not self.options[0].runtime_no_gui:
self.options[0].runtime_no_gui = True
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
# manage the CV API key
# None comparison is used so that the empty string can unset the value
if self.options[0]["comicvine"]["cv_api_key"] is not None or self.options[0]["comicvine"]["cv_url"] is not None:
settings_path = self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
if self.options[0].comicvine_cv_api_key is not None or self.options[0].comicvine_cv_url is not None:
settings_path = self.options[0].runtime_config.user_config_dir / "settings.json"
if self.config_load_success:
self.manager.save_file(self.options[0], settings_path)
if self.options[0]["commands"]["only_set_cv_key"]:
if self.options[0].commands_only_set_cv_key:
if self.config_load_success:
print("Key set") # noqa: T201
return
@ -149,13 +151,13 @@ class App:
try:
talker_api = ct_api.get_comic_talker("comicvine")( # type: ignore[call-arg]
version=version,
cache_folder=self.options[0]["runtime"]["config"].user_cache_dir,
series_match_thresh=self.options[0]["comicvine"]["series_match_search_thresh"],
remove_html_tables=self.options[0]["comicvine"]["remove_html_tables"],
use_series_start_as_volume=self.options[0]["comicvine"]["use_series_start_as_volume"],
wait_on_ratelimit=self.options[0]["autotag"]["wait_and_retry_on_rate_limit"],
api_url=self.options[0]["comicvine"]["cv_url"],
api_key=self.options[0]["comicvine"]["cv_api_key"],
cache_folder=self.options[0].runtime_config.user_cache_dir,
series_match_thresh=self.options[0].comicvine_series_match_search_thresh,
remove_html_tables=self.options[0].comicvine_remove_html_tables,
use_series_start_as_volume=self.options[0].comicvine_use_series_start_as_volume,
wait_on_ratelimit=self.options[0].autotag_wait_and_retry_on_rate_limit,
api_url=self.options[0].comicvine_cv_url,
api_key=self.options[0].comicvine_cv_api_key,
)
except TalkerError as e:
logger.exception("Unable to load talker")
@ -163,10 +165,10 @@ class App:
if not self.config_load_success:
error = (
f"Failed to load settings, check the log located in '{self.options[0]['runtime']['config'].user_log_dir}' for more details",
f"Failed to load settings, check the log located in '{self.options[0].runtime_config.user_log_dir}' for more details",
True,
)
if self.options[0]["runtime"]["no_gui"]:
if self.options[0].runtime_no_gui:
if error and error[1]:
print(f"A fatal error occurred please check the log for more information: {error[0]}") # noqa: T201
raise SystemExit(1)

View File

@ -47,10 +47,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.altCoverWidget = CoverImageWidget(
self.altCoverContainer,
CoverImageWidget.AltCoverMode,
options["runtime"]["config"].user_cache_dir,
talker_api,
self.altCoverContainer, CoverImageWidget.AltCoverMode, options.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
gridlayout.addWidget(self.altCoverWidget)

View File

@ -61,19 +61,19 @@ class RenameWindow(QtWidgets.QDialog):
self.rename_list: list[str] = []
self.btnSettings.clicked.connect(self.modify_settings)
platform = "universal" if self.options[0]["filename"]["rename_strict"] else "auto"
self.renamer = FileRenamer(None, platform=platform, replacements=self.options[0]["rename"]["replacements"])
platform = "universal" if self.options[0].filename_rename_strict else "auto"
self.renamer = FileRenamer(None, platform=platform, replacements=self.options[0].rename_replacements)
self.do_preview()
def config_renamer(self, ca: ComicArchive, md: GenericMetadata | None = None) -> str:
self.renamer.set_template(self.options[0]["filename"]["rename_template"])
self.renamer.set_issue_zero_padding(self.options[0]["filename"]["rename_issue_number_padding"])
self.renamer.set_smart_cleanup(self.options[0]["filename"]["rename_use_smart_string_cleanup"])
self.renamer.replacements = self.options[0]["rename"]["replacements"]
self.renamer.set_template(self.options[0].filename_rename_template)
self.renamer.set_issue_zero_padding(self.options[0].filename_rename_issue_number_padding)
self.renamer.set_smart_cleanup(self.options[0].filename_rename_use_smart_string_cleanup)
self.renamer.replacements = self.options[0].rename_replacements
new_ext = ca.path.suffix # default
if self.options[0]["filename"]["rename_set_extension_based_on_archive"]:
if self.options[0].filename_rename_set_extension_based_on_archive:
if ca.is_sevenzip():
new_ext = ".cb7"
elif ca.is_zip():
@ -85,13 +85,13 @@ class RenameWindow(QtWidgets.QDialog):
md = ca.read_metadata(self.data_style)
if md.is_empty:
md = ca.metadata_from_filename(
self.options[0]["filename"]["complicated_parser"],
self.options[0]["filename"]["remove_c2c"],
self.options[0]["filename"]["remove_fcbd"],
self.options[0]["filename"]["remove_publisher"],
self.options[0].filename_complicated_parser,
self.options[0].filename_remove_c2c,
self.options[0].filename_remove_fcbd,
self.options[0].filename_remove_publisher,
)
self.renamer.set_metadata(md)
self.renamer.move = self.options[0]["filename"]["rename_move_to_dir"]
self.renamer.move = self.options[0].filename_rename_move_to_dir
return new_ext
def do_preview(self) -> None:
@ -104,7 +104,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.options[0]["filename"]["rename_template"])
logger.exception("Invalid format string: %s", self.options[0].filename_rename_template)
QtWidgets.QMessageBox.critical(
self,
"Invalid format string!",
@ -119,7 +119,7 @@ class RenameWindow(QtWidgets.QDialog):
except Exception as e:
logger.exception(
"Formatter failure: %s metadata: %s",
self.options[0]["filename"]["rename_template"],
self.options[0].filename_rename_template,
self.renamer.metadata,
)
QtWidgets.QMessageBox.critical(
@ -197,9 +197,7 @@ class RenameWindow(QtWidgets.QDialog):
folder = get_rename_dir(
comic[0],
self.options[0]["filename"]["rename_dir"]
if self.options[0]["filename"]["rename_move_to_dir"]
else None,
self.options[0].filename_rename_dir if self.options[0].filename_rename_move_to_dir else None,
)
full_path = folder / comic[1]

View File

@ -269,54 +269,50 @@ class SettingsWindow(QtWidgets.QDialog):
def settings_to_form(self) -> None:
# Copy values from settings to form
self.leRarExePath.setText(self.options[0]["general"]["rar_exe_path"])
self.sbNameMatchIdentifyThresh.setValue(self.options[0]["identifier"]["series_match_identify_thresh"])
self.sbNameMatchSearchThresh.setValue(self.options[0]["comicvine"]["series_match_search_thresh"])
self.tePublisherFilter.setPlainText("\n".join(self.options[0]["identifier"]["publisher_filter"]))
self.leRarExePath.setText(self.options[0].general_rar_exe_path)
self.sbNameMatchIdentifyThresh.setValue(self.options[0].identifier_series_match_identify_thresh)
self.sbNameMatchSearchThresh.setValue(self.options[0].comicvine_series_match_search_thresh)
self.tePublisherFilter.setPlainText("\n".join(self.options[0].identifier_publisher_filter))
self.cbxCheckForNewVersion.setChecked(self.options[0]["general"]["check_for_new_version"])
self.cbxCheckForNewVersion.setChecked(self.options[0].general_check_for_new_version)
self.cbxComplicatedParser.setChecked(self.options[0]["filename"]["complicated_parser"])
self.cbxRemoveC2C.setChecked(self.options[0]["filename"]["remove_c2c"])
self.cbxRemoveFCBD.setChecked(self.options[0]["filename"]["remove_fcbd"])
self.cbxRemovePublisher.setChecked(self.options[0]["filename"]["remove_publisher"])
self.cbxComplicatedParser.setChecked(self.options[0].filename_complicated_parser)
self.cbxRemoveC2C.setChecked(self.options[0].filename_remove_c2c)
self.cbxRemoveFCBD.setChecked(self.options[0].filename_remove_fcbd)
self.cbxRemovePublisher.setChecked(self.options[0].filename_remove_publisher)
self.switch_parser()
self.cbxUseSeriesStartAsVolume.setChecked(self.options[0]["comicvine"]["use_series_start_as_volume"])
self.cbxClearFormBeforePopulating.setChecked(
self.options[0]["comicvine"]["clear_form_before_populating_from_cv"]
)
self.cbxRemoveHtmlTables.setChecked(self.options[0]["comicvine"]["remove_html_tables"])
self.cbxUseSeriesStartAsVolume.setChecked(self.options[0].comicvine_use_series_start_as_volume)
self.cbxClearFormBeforePopulating.setChecked(self.options[0].comicvine_clear_form_before_populating_from_cv)
self.cbxRemoveHtmlTables.setChecked(self.options[0].comicvine_remove_html_tables)
self.cbxUseFilter.setChecked(self.options[0]["comicvine"]["always_use_publisher_filter"])
self.cbxSortByYear.setChecked(self.options[0]["comicvine"]["sort_series_by_year"])
self.cbxExactMatches.setChecked(self.options[0]["comicvine"]["exact_series_matches_first"])
self.cbxUseFilter.setChecked(self.options[0].comicvine_always_use_publisher_filter)
self.cbxSortByYear.setChecked(self.options[0].comicvine_sort_series_by_year)
self.cbxExactMatches.setChecked(self.options[0].comicvine_exact_series_matches_first)
self.leKey.setText(self.options[0]["comicvine"]["cv_api_key"])
self.leURL.setText(self.options[0]["comicvine"]["cv_url"])
self.leKey.setText(self.options[0].comicvine_cv_api_key)
self.leURL.setText(self.options[0].comicvine_cv_url)
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.options[0]["cbl"]["assume_lone_credit_is_primary"])
self.cbxCopyCharactersToTags.setChecked(self.options[0]["cbl"]["copy_characters_to_tags"])
self.cbxCopyTeamsToTags.setChecked(self.options[0]["cbl"]["copy_teams_to_tags"])
self.cbxCopyLocationsToTags.setChecked(self.options[0]["cbl"]["copy_locations_to_tags"])
self.cbxCopyStoryArcsToTags.setChecked(self.options[0]["cbl"]["copy_storyarcs_to_tags"])
self.cbxCopyNotesToComments.setChecked(self.options[0]["cbl"]["copy_notes_to_comments"])
self.cbxCopyWebLinkToComments.setChecked(self.options[0]["cbl"]["copy_weblink_to_comments"])
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"])
self.cbxApplyCBLTransformOnBatchOperation.setChecked(
self.options[0]["cbl"]["apply_cbl_transform_on_bulk_operation"]
)
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.options[0].cbl_assume_lone_credit_is_primary)
self.cbxCopyCharactersToTags.setChecked(self.options[0].cbl_copy_characters_to_tags)
self.cbxCopyTeamsToTags.setChecked(self.options[0].cbl_copy_teams_to_tags)
self.cbxCopyLocationsToTags.setChecked(self.options[0].cbl_copy_locations_to_tags)
self.cbxCopyStoryArcsToTags.setChecked(self.options[0].cbl_copy_storyarcs_to_tags)
self.cbxCopyNotesToComments.setChecked(self.options[0].cbl_copy_notes_to_comments)
self.cbxCopyWebLinkToComments.setChecked(self.options[0].cbl_copy_weblink_to_comments)
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.options[0].cbl_apply_transform_on_import)
self.cbxApplyCBLTransformOnBatchOperation.setChecked(self.options[0].cbl_apply_transform_on_bulk_operation)
self.leRenameTemplate.setText(self.options[0]["rename"]["template"])
self.leIssueNumPadding.setText(str(self.options[0]["rename"]["issue_number_padding"]))
self.cbxSmartCleanup.setChecked(self.options[0]["rename"]["use_smart_string_cleanup"])
self.cbxChangeExtension.setChecked(self.options[0]["rename"]["set_extension_based_on_archive"])
self.cbxMoveFiles.setChecked(self.options[0]["rename"]["move_to_dir"])
self.leDirectory.setText(self.options[0]["rename"]["dir"])
self.cbxRenameStrict.setChecked(self.options[0]["rename"]["strict"])
self.leRenameTemplate.setText(self.options[0].rename_template)
self.leIssueNumPadding.setText(str(self.options[0].rename_issue_number_padding))
self.cbxSmartCleanup.setChecked(self.options[0].rename_use_smart_string_cleanup)
self.cbxChangeExtension.setChecked(self.options[0].rename_set_extension_based_on_archive)
self.cbxMoveFiles.setChecked(self.options[0].rename_move_to_dir)
self.leDirectory.setText(self.options[0].rename_dir)
self.cbxRenameStrict.setChecked(self.options[0].rename_strict)
for table, replacments in zip(
(self.twLiteralReplacements, self.twValueReplacements), self.options[0]["rename"]["replacements"]
(self.twLiteralReplacements, self.twValueReplacements), self.options[0].rename_replacements
):
table.clearContents()
for i in reversed(range(table.rowCount())):
@ -351,7 +347,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.options[0]["rename"]["template"])
logger.exception("Invalid format string: %s", self.options[0].rename_template)
QtWidgets.QMessageBox.critical(
self,
"Invalid format string!",
@ -365,7 +361,7 @@ class SettingsWindow(QtWidgets.QDialog):
return
else:
logger.exception(
"Formatter failure: %s metadata: %s", self.options[0]["rename"]["template"], self.renamer.metadata
"Formatter failure: %s metadata: %s", self.options[0].rename_template, self.renamer.metadata
)
QtWidgets.QMessageBox.critical(
self,
@ -378,71 +374,65 @@ class SettingsWindow(QtWidgets.QDialog):
)
# Copy values from form to settings and save
self.options[0]["general"]["rar_exe_path"] = str(self.leRarExePath.text())
self.options[0].general_rar_exe_path = str(self.leRarExePath.text())
# make sure rar program is now in the path for the rar class
if self.options[0]["general"]["rar_exe_path"]:
utils.add_to_path(os.path.dirname(self.options[0]["general"]["rar_exe_path"]))
if self.options[0].general_rar_exe_path:
utils.add_to_path(os.path.dirname(self.options[0].general_rar_exe_path))
if not str(self.leIssueNumPadding.text()).isdigit():
self.leIssueNumPadding.setText("0")
self.options[0]["general"]["check_for_new_version"] = self.cbxCheckForNewVersion.isChecked()
self.options[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked()
self.options[0]["identifier"]["series_match_identify_thresh"] = self.sbNameMatchIdentifyThresh.value()
self.options[0]["comicvine"]["series_match_search_thresh"] = self.sbNameMatchSearchThresh.value()
self.options[0]["identifier"]["publisher_filter"] = [
self.options[0].identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
self.options[0].comicvine_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
self.options[0].identifier_publisher_filter = [
x.strip() for x in str(self.tePublisherFilter.toPlainText()).splitlines() if x.strip()
]
self.options[0]["filename"]["complicated_parser"] = self.cbxComplicatedParser.isChecked()
self.options[0]["filename"]["remove_c2c"] = self.cbxRemoveC2C.isChecked()
self.options[0]["filename"]["remove_fcbd"] = self.cbxRemoveFCBD.isChecked()
self.options[0]["filename"]["remove_publisher"] = self.cbxRemovePublisher.isChecked()
self.options[0].filename_complicated_parser = self.cbxComplicatedParser.isChecked()
self.options[0].filename_remove_c2c = self.cbxRemoveC2C.isChecked()
self.options[0].filename_remove_fcbd = self.cbxRemoveFCBD.isChecked()
self.options[0].filename_remove_publisher = self.cbxRemovePublisher.isChecked()
self.options[0]["comicvine"]["use_series_start_as_volume"] = self.cbxUseSeriesStartAsVolume.isChecked()
self.options[0]["comicvine"][
"clear_form_before_populating_from_cv"
] = self.cbxClearFormBeforePopulating.isChecked()
self.options[0]["comicvine"]["remove_html_tables"] = self.cbxRemoveHtmlTables.isChecked()
self.options[0].comicvine_use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
self.options[0].comicvine_clear_form_before_populating_from_cv = self.cbxClearFormBeforePopulating.isChecked()
self.options[0].comicvine_remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
self.options[0]["comicvine"]["always_use_publisher_filter"] = self.cbxUseFilter.isChecked()
self.options[0]["comicvine"]["sort_series_by_year"] = self.cbxSortByYear.isChecked()
self.options[0]["comicvine"]["exact_series_matches_first"] = self.cbxExactMatches.isChecked()
self.options[0].comicvine_always_use_publisher_filter = self.cbxUseFilter.isChecked()
self.options[0].comicvine_sort_series_by_year = self.cbxSortByYear.isChecked()
self.options[0].comicvine_exact_series_matches_first = self.cbxExactMatches.isChecked()
if self.leKey.text().strip():
self.options[0]["comicvine"]["cv_api_key"] = self.leKey.text().strip()
self.talker_api.api_key = self.options[0]["comicvine"]["cv_api_key"]
self.options[0].comicvine_cv_api_key = self.leKey.text().strip()
self.talker_api.api_key = self.options[0].comicvine_cv_api_key
if self.leURL.text().strip():
self.options[0]["comicvine"]["cv_url"] = self.leURL.text().strip()
self.talker_api.api_url = self.options[0]["comicvine"]["cv_url"]
self.options[0].comicvine_cv_url = self.leURL.text().strip()
self.talker_api.api_url = self.options[0].comicvine_cv_url
self.options[0]["cbl"]["assume_lone_credit_is_primary"] = self.cbxAssumeLoneCreditIsPrimary.isChecked()
self.options[0]["cbl"]["copy_characters_to_tags"] = self.cbxCopyCharactersToTags.isChecked()
self.options[0]["cbl"]["copy_teams_to_tags"] = self.cbxCopyTeamsToTags.isChecked()
self.options[0]["cbl"]["copy_locations_to_tags"] = self.cbxCopyLocationsToTags.isChecked()
self.options[0]["cbl"]["copy_storyarcs_to_tags"] = self.cbxCopyStoryArcsToTags.isChecked()
self.options[0]["cbl"]["copy_notes_to_comments"] = self.cbxCopyNotesToComments.isChecked()
self.options[0]["cbl"]["copy_weblink_to_comments"] = self.cbxCopyWebLinkToComments.isChecked()
self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"] = self.cbxApplyCBLTransformOnCVIMport.isChecked()
self.options[0]["cbl"][
"apply_cbl_transform_on_bulk_operation"
] = self.cbxApplyCBLTransformOnBatchOperation.isChecked()
self.options[0].cbl_assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()
self.options[0].cbl_copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
self.options[0].cbl_copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
self.options[0].cbl_copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
self.options[0].cbl_copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
self.options[0].cbl_copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
self.options[0].cbl_copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
self.options[0].cbl_apply_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
self.options[0].cbl_apply_transform_on_bulk_operation = self.cbxApplyCBLTransformOnBatchOperation.isChecked()
self.options[0]["rename"]["template"] = str(self.leRenameTemplate.text())
self.options[0]["rename"]["issue_number_padding"] = int(self.leIssueNumPadding.text())
self.options[0]["rename"]["use_smart_string_cleanup"] = self.cbxSmartCleanup.isChecked()
self.options[0]["rename"]["set_extension_based_on_archive"] = self.cbxChangeExtension.isChecked()
self.options[0]["rename"]["move_to_dir"] = self.cbxMoveFiles.isChecked()
self.options[0]["rename"]["dir"] = self.leDirectory.text()
self.options[0].rename_template = str(self.leRenameTemplate.text())
self.options[0].rename_issue_number_padding = int(self.leIssueNumPadding.text())
self.options[0].rename_use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()
self.options[0].rename_set_extension_based_on_archive = self.cbxChangeExtension.isChecked()
self.options[0].rename_move_to_dir = self.cbxMoveFiles.isChecked()
self.options[0].rename_dir = self.leDirectory.text()
self.options[0]["rename"]["strict"] = self.cbxRenameStrict.isChecked()
self.options[0]["rename"]["replacements"] = self.get_replacemnts()
self.options[0].rename_strict = self.cbxRenameStrict.isChecked()
self.options[0].rename_replacements = self.get_replacemnts()
settngs.save_file(
self.options[0], self.options[1], self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
)
settngs.save_file(self.options, self.options[0].runtime_config.user_config_dir / "settings.json")
self.parent().options = self.options
QtWidgets.QDialog.accept(self)
@ -450,8 +440,8 @@ class SettingsWindow(QtWidgets.QDialog):
self.select_file(self.leRarExePath, "RAR")
def clear_cache(self) -> None:
ImageFetcher(self.options[0]["runtime"]["config"].cache_folder).clear_cache()
ComicCacher(self.options[0]["runtime"]["config"].cache_folder, version).clear_cache()
ImageFetcher(self.options[0].runtime_config.cache_folder).clear_cache()
ComicCacher(self.options[0].runtime_config.cache_folder, version).clear_cache()
QtWidgets.QMessageBox.information(self, self.name, "Cache has been cleared.")
def test_api_key(self) -> None:

View File

@ -87,18 +87,16 @@ class TaggerWindow(QtWidgets.QMainWindow):
uic.loadUi(ui_path / "taggerwindow.ui", self)
self.options = options
if not options:
self.options = ({}, {})
self.talker_api = talker_api
self.log_window = self.setup_logger()
# prevent multiple instances
socket = QtNetwork.QLocalSocket(self)
socket.connectToServer(options[0]["internal"]["install_id"])
socket.connectToServer(options[0].internal_install_id)
alive = socket.waitForConnected(3000)
if alive:
logger.setLevel(logging.INFO)
logger.info("Another application with key [%s] is already running", options[0]["internal"]["install_id"])
logger.info("Another application with key [%s] is already running", options[0].internal_install_id)
# send file list to other instance
if file_list:
socket.write(pickle.dumps(file_list))
@ -110,15 +108,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
# listen on a socket to prevent multiple instances
self.socketServer = QtNetwork.QLocalServer(self)
self.socketServer.newConnection.connect(self.on_incoming_socket_connection)
ok = self.socketServer.listen(options[0]["internal"]["install_id"])
ok = self.socketServer.listen(options[0].internal_install_id)
if not ok:
if self.socketServer.serverError() == QtNetwork.QAbstractSocket.SocketError.AddressInUseError:
self.socketServer.removeServer(options[0]["internal"]["install_id"])
ok = self.socketServer.listen(options[0]["internal"]["install_id"])
self.socketServer.removeServer(options[0].internal_install_id)
ok = self.socketServer.listen(options[0].internal_install_id)
if not ok:
logger.error(
"Cannot start local socket with key [%s]. Reason: %s",
options[0]["internal"]["install_id"],
options[0].internal_install_id,
self.socketServer.errorString(),
)
sys.exit()
@ -139,8 +137,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.fileSelectionList.selectionChanged.connect(self.file_list_selection_changed)
self.fileSelectionList.listCleared.connect(self.file_list_cleared)
self.fileSelectionList.set_sorting(
self.options[0]["internal"]["last_filelist_sorted_column"],
QtCore.Qt.SortOrder(self.options[0]["internal"]["last_filelist_sorted_order"]),
self.options[0].internal_sort_column,
QtCore.Qt.SortOrder(self.options[0].internal_sort_direction),
)
# we can't specify relative font sizes in the UI designer, so
@ -158,13 +156,13 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
if options[0]["runtime"]["type"] and isinstance(options[0]["runtime"]["type"][0], int):
if options[0].runtime_type and isinstance(options[0].runtime_type[0], int):
# respect the command line option tag type
options[0]["internal"]["last_selected_save_data_style"] = options[0]["runtime"]["type"][0]
options[0]["internal"]["last_selected_load_data_style"] = options[0]["runtime"]["type"][0]
options[0].internal_save_data_style = options[0].runtime_type[0]
options[0].internal_load_data_style = options[0].runtime_type[0]
self.save_data_style = options[0]["internal"]["last_selected_save_data_style"]
self.load_data_style = options[0]["internal"]["last_selected_load_data_style"]
self.save_data_style = options[0].internal_save_data_style
self.load_data_style = options[0].internal_load_data_style
self.setAcceptDrops(True)
self.config_menus()
@ -229,13 +227,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.show()
self.set_app_position()
if self.options[0]["internal"]["last_form_side_width"] != -1:
self.splitter.setSizes(
[
self.options[0]["internal"]["last_form_side_width"],
self.options[0]["internal"]["last_list_side_width"],
]
)
if self.options[0].internal_form_width != -1:
self.splitter.setSizes([self.options[0].internal_form_width, self.options[0].internal_list_width])
self.raise_()
QtCore.QCoreApplication.processEvents()
self.resizeEvent(None)
@ -252,7 +245,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
if len(file_list) != 0:
self.fileSelectionList.add_path_list(file_list)
if self.options[0]["dialog"]["show_disclaimer"]:
if self.options[0].dialog_show_disclaimer:
checked = OptionalMessageDialog.msg(
self,
"Welcome!",
@ -267,9 +260,9 @@ use ComicTagger on local copies of your comics.<br><br>
Have fun!
""",
)
self.options[0]["dialog"]["show_disclaimer"] = not checked
self.options[0].dialog_show_disclaimer = not checked
if self.options[0]["general"]["check_for_new_version"]:
if self.options[0].general_check_for_new_version:
self.check_latest_version_online()
def open_file_event(self, url: QtCore.QUrl) -> None:
@ -282,7 +275,7 @@ Have fun!
def setup_logger(self) -> ApplicationLogWindow:
try:
current_logs = (self.options[0]["runtime"]["config"].user_log_dir / "ComicTagger.log").read_text("utf-8")
current_logs = (self.options[0].runtime_config.user_log_dir / "ComicTagger.log").read_text("utf-8")
except Exception:
current_logs = ""
root_logger = logging.getLogger()
@ -615,10 +608,10 @@ Have fun!
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.options[0]["filename"]["complicated_parser"],
self.options[0]["filename"]["remove_c2c"],
self.options[0]["filename"]["remove_fcbd"],
self.options[0]["filename"]["remove_publisher"],
self.options[0].filename_complicated_parser,
self.options[0].filename_remove_c2c,
self.options[0].filename_remove_fcbd,
self.options[0].filename_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())
@ -986,10 +979,10 @@ Have fun!
# copy the form onto metadata object
self.form_to_metadata()
new_metadata = self.comic_archive.metadata_from_filename(
self.options[0]["filename"]["complicated_parser"],
self.options[0]["filename"]["remove_c2c"],
self.options[0]["filename"]["remove_fcbd"],
self.options[0]["filename"]["remove_publisher"],
self.options[0].filename_complicated_parser,
self.options[0].filename_remove_c2c,
self.options[0].filename_remove_fcbd,
self.options[0].filename_remove_publisher,
split_words,
)
if new_metadata is not None:
@ -1010,8 +1003,8 @@ Have fun!
else:
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFiles)
if self.options[0]["internal"]["last_opened_folder"] is not None:
dialog.setDirectory(self.options[0]["internal"]["last_opened_folder"])
if self.options[0].internal_last_opened_folder is not None:
dialog.setDirectory(self.options[0].internal_last_opened_folder)
if not folder_mode:
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar *.cb7 *.7z)"
@ -1093,10 +1086,10 @@ Have fun!
else:
QtWidgets.QApplication.restoreOverrideCursor()
if new_metadata is not None:
if self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"]:
if self.options[0].cbl_apply_transform_on_import:
new_metadata = CBLTransformer(new_metadata, self.options[0]).apply()
if self.options[0]["comicvine"]["clear_form_before_populating_from_cv"]:
if self.options[0].comicvine_clear_form_before_populating_from_cv:
self.clear_form()
notes = (
@ -1151,7 +1144,7 @@ Have fun!
"Change Tag Read Style", "If you change read tag style now, data in the form will be lost. Are you sure?"
):
self.load_data_style = self.cbLoadDataStyle.itemData(s)
self.options[0]["internal"]["last_selected_load_data_style"] = self.load_data_style
self.options[0].internal_load_data_style = self.load_data_style
self.update_menus()
if self.comic_archive is not None:
self.load_archive(self.comic_archive)
@ -1162,7 +1155,7 @@ Have fun!
def set_save_data_style(self, s: int) -> None:
self.save_data_style = self.cbSaveDataStyle.itemData(s)
self.options[0]["internal"]["last_selected_save_data_style"] = self.save_data_style
self.options[0].internal_save_data_style = self.save_data_style
self.update_style_tweaks()
self.update_menus()
@ -1387,14 +1380,9 @@ Have fun!
settingswin.result()
def set_app_position(self) -> None:
if self.options[0]["internal"]["last_main_window_width"] != 0:
self.move(
self.options[0]["internal"]["last_main_window_x"], self.options[0]["internal"]["last_main_window_y"]
)
self.resize(
self.options[0]["internal"]["last_main_window_width"],
self.options[0]["internal"]["last_main_window_height"],
)
if self.options[0].internal_window_width != 0:
self.move(self.options[0].internal_window_x, self.options[0].internal_window_y)
self.resize(self.options[0].internal_window_width, self.options[0].internal_window_height)
else:
screen = QtGui.QGuiApplication.primaryScreen().geometry()
size = self.frameGeometry()
@ -1661,10 +1649,7 @@ Have fun!
if ca.has_metadata(src_style) and ca.is_writable():
md = ca.read_metadata(src_style)
if (
dest_style == MetaDataStyle.CBI
and self.options[0]["cbl"]["apply_cbl_transform_on_bulk_operation"]
):
if dest_style == MetaDataStyle.CBI and self.options[0].cbl_apply_transform_on_bulk_operation:
md = CBLTransformer(md, self.options[0]).apply()
if not ca.write_metadata(md, dest_style):
@ -1703,7 +1688,7 @@ Have fun!
logger.exception("Save aborted.")
if not ct_md.is_empty:
if self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"]:
if self.options[0].cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options[0]).apply()
QtWidgets.QApplication.restoreOverrideCursor()
@ -1733,10 +1718,10 @@ Have fun!
logger.error("Failed to load metadata for %s: %s", ca.path, e)
if md.is_empty:
md = ca.metadata_from_filename(
self.options[0]["filename"]["complicated_parser"],
self.options[0]["filename"]["remove_c2c"],
self.options[0]["filename"]["remove_fcbd"],
self.options[0]["filename"]["remove_publisher"],
self.options[0].filename_complicated_parser,
self.options[0].filename_remove_c2c,
self.options[0].filename_remove_fcbd,
self.options[0].filename_remove_publisher,
dlg.split_words,
)
if dlg.ignore_leading_digits_in_filename and md.series is not None:
@ -1822,7 +1807,7 @@ Have fun!
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.options[0]["comicvine"]["auto_imprint"]:
if self.options[0].comicvine_auto_imprint:
md.fix_publisher()
if not ca.write_metadata(md, self.save_data_style):
@ -2000,19 +1985,17 @@ Have fun!
f"Exit {self.appName}", "If you quit now, data in the form will be lost. Are you sure?"
):
appsize = self.size()
self.options[0]["internal"]["last_main_window_width"] = appsize.width()
self.options[0]["internal"]["last_main_window_height"] = appsize.height()
self.options[0]["internal"]["last_main_window_x"] = self.x()
self.options[0]["internal"]["last_main_window_y"] = self.y()
self.options[0]["internal"]["last_form_side_width"] = self.splitter.sizes()[0]
self.options[0]["internal"]["last_list_side_width"] = self.splitter.sizes()[1]
self.options[0].internal_window_width = appsize.width()
self.options[0].internal_window_height = appsize.height()
self.options[0].internal_window_x = self.x()
self.options[0].internal_window_y = self.y()
self.options[0].internal_form_width = self.splitter.sizes()[0]
self.options[0].internal_list_width = self.splitter.sizes()[1]
(
self.options[0]["internal"]["last_filelist_sorted_column"],
self.options[0]["internal"]["last_filelist_sorted_order"],
self.options[0].internal_sort_column,
self.options[0].internal_sort_direction,
) = self.fileSelectionList.get_sorting()
settngs.save_file(
self.options[0], self.options[1], self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
)
settngs.save_file(self.options, self.options[0].runtime_config.user_config_dir / "settings.json")
event.accept()
else:
@ -2106,7 +2089,7 @@ Have fun!
QtCore.QTimer.singleShot(1, self.fileSelectionList.revert_selection)
return
self.options[0]["internal"]["last_opened_folder"] = os.path.abspath(os.path.split(comic_archive.path)[0])
self.options[0].internal_last_opened_folder = os.path.abspath(os.path.split(comic_archive.path)[0])
self.comic_archive = comic_archive
try:
self.metadata = self.comic_archive.read_metadata(self.load_data_style)
@ -2139,12 +2122,12 @@ Have fun!
version_checker = VersionChecker()
self.version_check_complete(
version_checker.get_latest_version(
self.options[0]["internal"]["install_id"], self.options[0]["general"]["send_usage_stats"]
self.options[0].internal_install_id, self.options[0].general_send_usage_stats
)
)
def version_check_complete(self, new_version: tuple[str, str]) -> None:
if new_version[0] not in (self.version, self.options[0]["dialog"]["dont_notify_about_this_version"]):
if new_version[0] not in (self.version, self.options[0].dialog_dont_notify_about_this_version):
website = "https://github.com/comictagger/comictagger"
checked = OptionalMessageDialog.msg(
self,
@ -2155,7 +2138,7 @@ Have fun!
"Don't tell me about this version again",
)
if checked:
self.options[0]["dialog"]["dont_notify_about_this_version"] = new_version[0]
self.options[0].dialog_dont_notify_about_this_version = new_version[0]
def on_incoming_socket_connection(self) -> None:
# Accept connection from other instance.

View File

@ -111,7 +111,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
issue_count: int,
cover_index_list: list[int],
comic_archive: ComicArchive | None,
options: settngs.ConfigValues,
options: settngs.Namespace,
talker_api: ComicTalker,
autoselect: bool = False,
literal: bool = False,
@ -121,7 +121,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
uic.loadUi(ui_path / "volumeselectionwindow.ui", self)
self.imageWidget = CoverImageWidget(
self.imageContainer, CoverImageWidget.URLMode, options["runtime"]["config"].user_cache_dir, talker_api
self.imageContainer, CoverImageWidget.URLMode, options.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.imageContainer)
gridlayout.addWidget(self.imageWidget)
@ -156,7 +156,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
self.progdialog: QtWidgets.QProgressDialog | None = None
self.search_thread: SearchThread | None = None
self.use_filter = self.options["comicvine"]["always_use_publisher_filter"]
self.use_filter = self.options.comicvine_always_use_publisher_filter
# Load to retrieve settings
self.talker_api = talker_api
@ -326,11 +326,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
def perform_query(self, refresh: bool = False) -> None:
self.search_thread = SearchThread(
self.talker_api,
self.series_name,
refresh,
self.literal,
self.options["comicvine"]["series_match_search_thresh"],
self.talker_api, self.series_name, refresh, self.literal, self.options.comicvine_series_match_search_thresh
)
self.search_thread.searchComplete.connect(self.search_complete)
self.search_thread.progressUpdate.connect(self.search_progress_update)
@ -382,7 +378,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
# filter the publishers if enabled set
if self.use_filter:
try:
publisher_filter = {s.strip().casefold() for s in self.options["identifier"]["publisher_filter"]}
publisher_filter = {s.strip().casefold() for s in self.options.identifier_publisher_filter}
# use '' as publisher name if None
self.ct_search_results = list(
filter(
@ -398,7 +394,7 @@ class VolumeSelectionWindow(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.options["comicvine"]["sort_series_by_year"]:
if self.options.comicvine_sort_series_by_year:
try:
self.ct_search_results = sorted(
self.ct_search_results,
@ -416,7 +412,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
logger.exception("bad data error sorting results by count_of_issues")
# move sanitized matches to the front
if self.options["comicvine"]["exact_series_matches_first"]:
if self.options.comicvine_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()

View File

@ -8,7 +8,7 @@ pycountry
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
rapidfuzz>=2.12.0
requests==2.*
settngs==0.2.0
settngs==0.3.0
text2digits
typing_extensions
wordninja

View File

@ -7,8 +7,9 @@ from testing.comicdata import search_results
def test_create_cache(options, mock_version):
comictalker.comiccacher.ComicCacher(options["runtime"]["config"].user_cache_dir, mock_version[0])
assert (options["runtime"]["config"].user_cache_dir).exists()
settings, definitions = options
comictalker.comiccacher.ComicCacher(settings.runtime_config.user_cache_dir, mock_version[0])
assert (settings.runtime_config.user_cache_dir).exists()
def test_search_results(comic_cache):

View File

@ -117,7 +117,7 @@ def comicvine_api(
cv = comictalker.talkers.comicvine.ComicVineTalker(
version=mock_version[0],
cache_folder=options["runtime"]["config"].user_cache_dir,
cache_folder=options[0].runtime_config.user_cache_dir,
api_url="",
api_key="",
series_match_thresh=90,
@ -167,13 +167,13 @@ def options(settings_manager, tmp_path):
comictaggerlib.ctoptions.register_commandline(settings_manager)
comictaggerlib.ctoptions.register_settings(settings_manager)
defaults = settings_manager.defaults()
defaults["runtime"]["config"] = comictaggerlib.ctoptions.ComicTaggerPaths(tmp_path / "config")
defaults["runtime"]["config"].user_data_dir.mkdir(parents=True, exist_ok=True)
defaults["runtime"]["config"].user_config_dir.mkdir(parents=True, exist_ok=True)
defaults["runtime"]["config"].user_cache_dir.mkdir(parents=True, exist_ok=True)
defaults["runtime"]["config"].user_state_dir.mkdir(parents=True, exist_ok=True)
defaults["runtime"]["config"].user_log_dir.mkdir(parents=True, exist_ok=True)
defaults = settings_manager.get_namespace(settings_manager.defaults())
defaults[0].runtime_config = comictaggerlib.ctoptions.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)
yield defaults
@ -185,4 +185,4 @@ def settings_manager():
@pytest.fixture
def comic_cache(options, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
yield comictalker.comiccacher.ComicCacher(options["runtime"]["config"].user_cache_dir, mock_version[0])
yield comictalker.comiccacher.ComicCacher(options[0].runtime_config.user_cache_dir, mock_version[0])

View File

@ -10,7 +10,8 @@ import testing.comicvine
def test_crop(cbz_double_cover, options, tmp_path, comicvine_api):
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, options, comicvine_api)
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, settings, comicvine_api)
cropped = ii.crop_cover(cbz_double_cover.archiver.read_file("double_cover.jpg"))
original_cover = cbz_double_cover.get_page(0)
@ -22,14 +23,16 @@ def test_crop(cbz_double_cover, options, tmp_path, comicvine_api):
@pytest.mark.parametrize("additional_md, expected", testing.comicdata.metadata_keys)
def test_get_search_keys(cbz, options, additional_md, expected, comicvine_api):
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
ii.set_additional_metadata(additional_md)
assert expected == ii.get_search_keys()
def test_get_issue_cover_match_score(cbz, options, comicvine_api):
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
score = ii.get_issue_cover_match_score(
int(
comicapi.issuestring.IssueString(
@ -50,7 +53,8 @@ def test_get_issue_cover_match_score(cbz, options, comicvine_api):
def test_search(cbz, options, comicvine_api):
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
results = ii.search()
cv_expected = {
"series": f"{testing.comicvine.cv_volume_result['results']['name']} ({testing.comicvine.cv_volume_result['results']['start_year']})",