Use consistent naming for settings

config: always values
setting: always the definition/description not the value
This commit is contained in:
Timmy Welch 2023-01-31 20:21:39 -08:00
parent ba4b779145
commit ad68726e1d
No known key found for this signature in database
26 changed files with 489 additions and 491 deletions

View File

@ -40,19 +40,19 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
match_set_list: list[MultipleMatch],
style: int,
fetch_func: Callable[[IssueResult], GenericMetadata],
options: settngs.Namespace,
config: settngs.Namespace,
talker_api: ComicTalker,
) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.options = options
self.config = config
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, config.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
gridlayout.addWidget(self.altCoverWidget)
@ -240,10 +240,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.config.filename_complicated_parser,
self.config.filename_remove_c2c,
self.config.filename_remove_fcbd,
self.config.filename_remove_publisher,
)
# now get the particular issue data

View File

@ -1,4 +1,4 @@
"""A PyQT4 dialog to confirm and set options for auto-tag"""
"""A PyQT4 dialog to confirm and set config for auto-tag"""
#
# Copyright 2012-2014 Anthony Beville
#
@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
class AutoTagStartWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, options: settngs.Namespace, msg: str) -> None:
def __init__(self, parent: QtWidgets.QWidget, config: settngs.Namespace, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "autotagstartwindow.ui", self)
@ -36,20 +36,20 @@ class AutoTagStartWindow(QtWidgets.QDialog):
QtCore.Qt.WindowType(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
)
self.options = options
self.config = config
self.cbxSpecifySearchString.setChecked(False)
self.cbxSplitWords.setChecked(False)
self.sbNameMatchSearchThresh.setValue(self.options.identifier_series_match_identify_thresh)
self.sbNameMatchSearchThresh.setValue(self.config.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.talkers_auto_imprint)
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.cbxWaitForRateLimit.setChecked(self.config.autotag_wait_and_retry_on_rate_limit)
self.cbxAutoImprint.setChecked(self.config.talkers_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.talkers_series_match_search_thresh
self.name_length_match_tolerance = self.config.talkers_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.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.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,9 +25,9 @@ logger = logging.getLogger(__name__)
class CBLTransformer:
def __init__(self, metadata: GenericMetadata, options: settngs.Namespace) -> None:
def __init__(self, metadata: GenericMetadata, config: settngs.Namespace) -> None:
self.metadata = metadata
self.options = options
self.config = config
def apply(self) -> GenericMetadata:
# helper funcs
@ -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.config.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.config.cbl_copy_characters_to_tags:
add_string_list_to_tags(self.metadata.characters)
if self.options.cbl_copy_teams_to_tags:
if self.config.cbl_copy_teams_to_tags:
add_string_list_to_tags(self.metadata.teams)
if self.options.cbl_copy_locations_to_tags:
if self.config.cbl_copy_locations_to_tags:
add_string_list_to_tags(self.metadata.locations)
if self.options.cbl_copy_storyarcs_to_tags:
if self.config.cbl_copy_storyarcs_to_tags:
add_string_list_to_tags(self.metadata.story_arc)
if self.options.cbl_copy_notes_to_comments:
if self.config.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.config.cbl_copy_weblink_to_comments:
if self.metadata.web_link is not None:
if self.metadata.comments is None:
self.metadata.comments = ""

View File

@ -40,8 +40,8 @@ logger = logging.getLogger(__name__)
class CLI:
def __init__(self, options: settngs.Values, talker_api: ComicTalker):
self.options = options
def __init__(self, config: settngs.Values, talker_api: ComicTalker):
self.config = config
self.talker_api = talker_api
self.batch_mode = False
@ -53,14 +53,14 @@ class CLI:
logger.exception(f"Error retrieving issue details. Save aborted.\n{e}")
return GenericMetadata()
if self.options.cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options).apply()
if self.config.cbl_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.options.runtime_dryrun:
for metadata_style in self.options.runtime_type:
if not self.config.runtime_dryrun:
for metadata_style in self.config.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_quiet:
if self.config.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.config.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.talkers_clear_metadata_on_import:
if self.config.talkers_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.talkers_auto_imprint:
if self.config.talkers_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_summary:
if self.config.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_summary and not self.options.runtime_interactive:
if not self.config.runtime_summary and not self.config.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.config.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.config.runtime_file_list) > 1
for f in self.options.runtime_file_list:
for f in self.config.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.config.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.config.filename_complicated_parser,
self.config.filename_remove_c2c,
self.config.filename_remove_fcbd,
self.config.filename_remove_publisher,
self.config.runtime_split_words,
)
md.overlay(f_md)
for metadata_style in self.options.runtime_type:
for metadata_style in self.config.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.config.runtime_metadata)
return md
def print(self, ca: ComicArchive) -> None:
if not self.options.runtime_type:
if not self.config.runtime_type:
page_count = ca.get_number_of_pages()
brief = ""
@ -239,38 +239,38 @@ class CLI:
print(brief)
if self.options.runtime_quiet:
if self.config.runtime_quiet:
return
print()
if not self.options.runtime_type or MetaDataStyle.CIX in self.options.runtime_type:
if not self.config.runtime_type or MetaDataStyle.CIX in self.config.runtime_type:
if ca.has_metadata(MetaDataStyle.CIX):
print("--------- ComicRack tags ---------")
try:
if self.options.runtime_raw:
if self.config.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.config.runtime_type or MetaDataStyle.CBI in self.config.runtime_type:
if ca.has_metadata(MetaDataStyle.CBI):
print("------- ComicBookLover tags -------")
try:
if self.options.runtime_raw:
if self.config.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.config.runtime_type or MetaDataStyle.COMET in self.config.runtime_type:
if ca.has_metadata(MetaDataStyle.COMET):
print("----------- CoMet tags -----------")
try:
if self.options.runtime_raw:
if self.config.runtime_raw:
print(ca.read_raw_comet())
else:
print(ca.read_comet())
@ -278,10 +278,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.config.runtime_type:
style_name = MetaDataStyle.name[metadata_style]
if ca.has_metadata(metadata_style):
if not self.options.runtime_dryrun:
if not self.config.runtime_dryrun:
if not ca.remove_metadata(metadata_style):
print(f"{ca.path}: Tag removal seemed to fail!")
else:
@ -292,26 +292,26 @@ 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.config.runtime_type:
dst_style_name = MetaDataStyle.name[metadata_style]
if not self.options.runtime_overwrite and ca.has_metadata(metadata_style):
if not self.config.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.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.options.commands_copy]
if ca.has_metadata(self.options.commands_copy):
if not self.options.runtime_dryrun:
src_style_name = MetaDataStyle.name[self.config.commands_copy]
if ca.has_metadata(self.config.commands_copy):
if not self.config.runtime_dryrun:
try:
md = ca.read_metadata(self.options.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.options.apply_transform_on_bulk_operation_ndetadata_style == MetaDataStyle.CBI:
md = CBLTransformer(md, self.options).apply()
if self.config.apply_transform_on_bulk_operation_ndetadata_style == MetaDataStyle.CBI:
md = CBLTransformer(md, self.config).apply()
if not ca.write_metadata(md, metadata_style):
print(f"{ca.path}: Tag copy seemed to fail!")
@ -323,8 +323,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.options.runtime_overwrite:
for metadata_style in self.options.runtime_type:
if not self.config.runtime_overwrite:
for metadata_style in self.config.runtime_type:
if ca.has_metadata(metadata_style):
print(f"{ca.path}: Already has {MetaDataStyle.name[metadata_style]} tags. Not overwriting.")
return
@ -334,37 +334,37 @@ 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.config.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.config.runtime_online:
if self.config.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.config.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.config.runtime_issue_id)
match_results.no_matches.append(str(ca.path.absolute()))
return
if self.options.cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options).apply()
if self.config.cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.config).apply()
else:
if md is None or md.is_empty:
logger.error("No metadata given to search online with!")
match_results.no_matches.append(str(ca.path.absolute()))
return
ii = IssueIdentifier(ca, self.options, self.talker_api)
ii = IssueIdentifier(ca, self.config, self.talker_api)
def myoutput(text: str) -> None:
if self.options.runtime_verbose:
if self.config.runtime_verbose:
IssueIdentifier.default_write_output(text)
# use our overlaid MD struct to search
@ -404,7 +404,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.config.runtime_abort_on_low_confidence:
logger.error("Online search: Low confidence match. Save aborted")
match_results.low_confidence_matches.append(MultipleMatch(ca, matches))
return
@ -421,7 +421,7 @@ class CLI:
match_results.fetch_data_failures.append(str(ca.path.absolute()))
return
if self.options.talkers_clear_metadata_on_import:
if self.config.talkers_clear_metadata_on_import:
md = ct_md
else:
notes = (
@ -430,7 +430,7 @@ class CLI:
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.options.talkers_auto_imprint:
if self.config.talkers_auto_imprint:
md.fix_publisher()
# ok, done building our metadata. time to save
@ -452,18 +452,18 @@ class CLI:
return
new_ext = "" # default
if self.options.filename_rename_set_extension_based_on_archive:
if self.config.filename_rename_set_extension_based_on_archive:
new_ext = ca.extension()
renamer = FileRenamer(
md,
platform="universal" if self.options.filename_rename_strict else "auto",
replacements=self.options.rename_replacements,
platform="universal" if self.config.filename_rename_strict else "auto",
replacements=self.config.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.config.filename_rename_template)
renamer.set_issue_zero_padding(self.config.filename_rename_issue_number_padding)
renamer.set_smart_cleanup(self.config.filename_rename_use_smart_string_cleanup)
renamer.move = self.config.filename_rename_move_to_dir
try:
new_name = renamer.determine_name(ext=new_ext)
@ -475,16 +475,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.config.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.config.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.config.filename_rename_dir if self.config.filename_rename_move_to_dir else None
)
full_path = folder / new_name
@ -494,7 +494,7 @@ class CLI:
return
suffix = ""
if not self.options.runtime_dryrun:
if not self.config.runtime_dryrun:
# rename the file
try:
ca.rename(utils.unique_file(full_path))
@ -517,7 +517,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.config.runtime_abort_on_conflict and new_file.exists():
print(msg_hdr + f"{new_file.name} already exists in the that folder.")
return
@ -525,10 +525,10 @@ class CLI:
delete_success = False
export_success = False
if not self.options.runtime_dryrun:
if not self.config.runtime_dryrun:
if ca.export_as_zip(new_file):
export_success = True
if self.options.runtime_delete_after_zip_export:
if self.config.runtime_delete_after_zip_export:
try:
filename_path.unlink(missing_ok=True)
delete_success = True
@ -540,7 +540,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.config.runtime_delete_after_zip_export:
msg += " and delete original."
print(msg)
return
@ -548,7 +548,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.config.runtime_delete_after_zip_export and delete_success:
msg += " (Original deleted) "
else:
msg += "Archive failed to export!"
@ -567,28 +567,28 @@ class CLI:
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.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.options.commands_print:
if self.config.commands_print:
self.print(ca)
elif self.options.commands_delete:
elif self.config.commands_delete:
self.delete(ca)
elif self.options.commands_copy is not None:
elif self.config.commands_copy is not None:
self.copy(ca)
elif self.options.commands_save:
elif self.config.commands_save:
self.save(ca, match_results)
elif self.options.commands_rename:
elif self.config.commands_rename:
self.rename(ca)
elif self.options.commands_export_to_zip:
elif self.config.commands_export_to_zip:
self.export(ca)

View File

@ -1,21 +0,0 @@
from __future__ import annotations
from comictaggerlib.ctoptions.cmdline import initial_cmd_line_parser, register_commandline, validate_commandline_options
from comictaggerlib.ctoptions.file import register_settings, validate_settings
from comictaggerlib.ctoptions.plugin import register_plugin_settings, validate_plugin_settings
from comictaggerlib.ctoptions.types import ComicTaggerPaths
from comictalker.talkerbase import ComicTalker
talker_plugins: dict[str, ComicTalker] = {}
__all__ = [
"initial_cmd_line_parser",
"register_commandline",
"register_settings",
"register_plugin_settings",
"register_talker_settings",
"validate_commandline_options",
"validate_settings",
"validate_plugin_settings",
"ComicTaggerPaths",
]

View File

@ -0,0 +1,24 @@
from __future__ import annotations
from comictaggerlib.ctsettings.commandline import (
initial_commandline_parser,
register_commandline_settings,
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.types import ComicTaggerPaths
from comictalker.talkerbase import ComicTalker
talkers: dict[str, ComicTalker] = {}
__all__ = [
"initial_commandline_parser",
"register_commandline_settings",
"register_file_settings",
"register_plugin_settings",
"validate_commandline_settings",
"validate_file_settings",
"validate_plugin_settings",
"ComicTaggerPaths",
]

View File

@ -1,4 +1,4 @@
"""CLI options class for ComicTagger app"""
"""CLI settings for ComicTagger"""
#
# Copyright 2012-2014 Anthony Beville
#
@ -25,14 +25,14 @@ import settngs
from comicapi import utils
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib import ctversion
from comictaggerlib.ctoptions.types import ComicTaggerPaths, metadata_type, parse_metadata_from_string
from comictaggerlib.ctsettings.types import ComicTaggerPaths, metadata_type, parse_metadata_from_string
logger = logging.getLogger(__name__)
def initial_cmd_line_parser() -> argparse.ArgumentParser:
def initial_commandline_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(add_help=False)
# Ensure this stays up to date with register_options
# Ensure this stays up to date with register_settings
parser.add_argument(
"--config",
help="Config directory defaults to ~/.ComicTagger\non Linux/Mac and %%APPDATA%% on Windows\n",
@ -49,7 +49,7 @@ def initial_cmd_line_parser() -> argparse.ArgumentParser:
return parser
def register_options(parser: settngs.Manager) -> None:
def register_settings(parser: settngs.Manager) -> None:
parser.add_setting(
"--config",
help="Config directory defaults to ~/.Config/ComicTagger\non Linux, ~/Library/Application Support/ComicTagger on Mac and %%APPDATA%%\\ComicTagger on Windows\n",
@ -262,68 +262,61 @@ def register_commands(parser: settngs.Manager) -> None:
)
def register_commandline(parser: settngs.Manager) -> None:
def register_commandline_settings(parser: settngs.Manager) -> None:
parser.add_group("commands", register_commands, True)
parser.add_group("runtime", register_options)
parser.add_group("runtime", register_settings)
def validate_commandline_options(options: settngs.Config[settngs.Values], parser: settngs.Manager) -> settngs.Values:
def validate_commandline_settings(config: settngs.Config[settngs.Values], parser: settngs.Manager) -> settngs.Values:
if options[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",
)
options[0].runtime_no_gui = any(
config[0].runtime_no_gui = any(
[
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,
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,
]
)
if platform.system() == "Windows" and options[0].runtime_glob:
if platform.system() == "Windows" and config[0].runtime_glob:
# no globbing on windows shell, so do it for them
import glob
globs = options[0].runtime_files
options[0].runtime_files = []
globs = config[0].runtime_files
config[0].runtime_files = []
for item in globs:
options[0].runtime_files.extend(glob.glob(item))
config[0].runtime_files.extend(glob.glob(item))
if (
options[0].commands_only_set_cv_key
and options[0].talker_comicvine_cv_api_key is None
and options[0].talker_comicvine_cv_url is None
):
parser.exit(message="Key not given!\n", status=1)
if not options[0].commands_only_set_cv_key and options[0].runtime_no_gui and not options[0].runtime_files:
if not config[0].commands_only_set_cv_key and config[0].runtime_no_gui and not config[0].runtime_files:
parser.exit(message="Command requires at least one filename!\n", status=1)
if options[0].commands_delete and not options[0].runtime_type:
if config[0].commands_delete and not config[0].runtime_type:
parser.exit(message="Please specify the type to delete with -t\n", status=1)
if options[0].commands_save and not options[0].runtime_type:
if config[0].commands_save and not config[0].runtime_type:
parser.exit(message="Please specify the type to save with -t\n", status=1)
if options[0].commands_copy:
if not options[0].runtime_type:
if config[0].commands_copy:
if not config[0].runtime_type:
parser.exit(message="Please specify the type to copy to with -t\n", status=1)
if len(options[0].commands_copy) > 1:
if len(config[0].commands_copy) > 1:
parser.exit(message="Please specify only one type to copy to with -c\n", status=1)
options[0].commands_copy = options[0].commands_copy[0]
config[0].commands_copy = config[0].commands_copy[0]
if options[0].runtime_recursive:
options[0].runtime_file_list = utils.get_recursive_filelist(options[0].runtime_files)
if config[0].runtime_recursive:
config[0].runtime_file_list = utils.get_recursive_filelist(config[0].runtime_files)
else:
options[0].runtime_file_list = options[0].runtime_files
config[0].runtime_file_list = config[0].runtime_files
# take a crack at finding rar exe if it's not in the path
if not utils.which("rar"):
@ -337,4 +330,4 @@ def validate_commandline_options(options: settngs.Config[settngs.Values], parser
if os.path.exists("/opt/homebrew/bin"):
utils.add_to_path("/opt/homebrew/bin")
return options
return config

View File

@ -6,7 +6,7 @@ from typing import Any
import settngs
from comictaggerlib.ctoptions.types import AppendAction
from comictaggerlib.ctsettings.types import AppendAction
from comictaggerlib.defaults import DEFAULT_REPLACEMENTS, Replacement, Replacements
@ -225,16 +225,16 @@ def autotag(parser: settngs.Manager) -> None:
)
def validate_settings(options: settngs.Config[settngs.Values]) -> 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]],
def validate_file_settings(config: settngs.Config[settngs.Values]) -> dict[str, dict[str, Any]]:
config[0].identifier_publisher_filter = [x.strip() for x in config[0].identifier_publisher_filter if x.strip()]
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]],
)
return options
return config
def register_settings(parser: settngs.Manager) -> None:
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)

View File

@ -6,7 +6,7 @@ import os
import settngs
import comicapi.comicarchive
import comictaggerlib.ctoptions
import comictaggerlib.ctsettings
logger = logging.getLogger("comictagger")
@ -24,17 +24,17 @@ def archiver(manager: settngs.Manager) -> None:
def register_talker_settings(manager: settngs.Manager) -> None:
for talker_name, talker in comictaggerlib.ctoptions.talker_plugins.items():
for talker_name, talker in comictaggerlib.ctsettings.talkers.items():
try:
manager.add_persistent_group("talker_" + talker_name, talker.register_settings, False)
except Exception:
logger.exception("Failed to register settings for %s", talker_name)
def validate_archive_settings(options: settngs.Config) -> settngs.Config:
if "archiver" not in options[1]:
return options
cfg = settngs.normalize_config(options, file=True, cmdline=True, defaults=False)
def validate_archive_settings(config: settngs.Config[settngs.Namespace]) -> settngs.Config[settngs.Namespace]:
if "archiver" not in config[1]:
return config
cfg = settngs.normalize_config(config, file=True, cmdline=True, defaults=False)
for archiver in comicapi.comicarchive.archivers:
exe_name = settngs.sanitize_name(archiver.exe)
if (
@ -47,28 +47,29 @@ def validate_archive_settings(options: settngs.Config) -> settngs.Config:
else:
archiver.exe = cfg[0]["archiver"][exe_name]
return options
return config
def validate_talker_settings(options: settngs.Config) -> settngs.Config:
def validate_talker_settings(config: settngs.Config[settngs.Namespace]) -> settngs.Config[settngs.Namespace]:
# Apply talker settings from config file
for talker_name, talker in list(comictaggerlib.ctoptions.talker_plugins.items()):
cfg = settngs.normalize_config(config, True, True)
for talker_name, talker in list(comictaggerlib.ctsettings.talkers.items()):
try:
talker.parse_settings(options[0]["talker_" + talker_name])
talker.parse_settings(cfg[0]["talker_" + talker_name])
except Exception as e:
# Remove talker as we failed to apply the settings
del comictaggerlib.ctoptions.talker_plugins[talker_name]
del comictaggerlib.ctsettings.talkers[talker_name]
logger.exception("Failed to initialize talker settings: %s", e)
return options
return config
def validate_plugin_settings(options: settngs.Config) -> settngs.Config:
options = validate_archive_settings(options)
options = validate_talker_settings(options)
return options
def validate_plugin_settings(config: settngs.Config[settngs.Namespace]) -> settngs.Config[settngs.Namespace]:
config = validate_archive_settings(config)
config = validate_talker_settings(config)
return config
def register_plugin_settings(manager: settngs.Manager):
def register_plugin_settings(manager: settngs.Manager) -> None:
manager.add_persistent_group("archiver", archiver, False)
register_talker_settings(manager)

View File

@ -57,13 +57,13 @@ class FileSelectionList(QtWidgets.QWidget):
dataColNum = fileColNum
def __init__(
self, parent: QtWidgets.QWidget, options: settngs.Namespace, dirty_flag_verification: Callable[[str, str], bool]
self, parent: QtWidgets.QWidget, config: settngs.Namespace, dirty_flag_verification: Callable[[str, str], bool]
) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "fileselectionlist.ui", self)
self.options = options
self.config = config
reduce_widget_font_size(self.twList)

View File

@ -83,11 +83,11 @@ except ImportError as e:
def open_tagger_window(
talker_api: ComicTalker, options: settngs.Config[settngs.Namespace], error: tuple[str, bool] | None
talker_api: ComicTalker | None, config: 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 config[0].runtime_darkmode:
args.extend(["-platform", "windows:darkmode=2"])
args.extend(sys.argv)
app = Application(args)
@ -96,8 +96,10 @@ def open_tagger_window(
if error[1]:
raise SystemExit(1)
assert talker_api is not None
# 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: config[0].runtime_files.append(x.toLocalFile()))
if platform.system() == "Darwin":
# Set the MacOS dock icon
@ -125,7 +127,7 @@ def open_tagger_window(
QtWidgets.QApplication.processEvents()
try:
tagger_window = TaggerWindow(options[0].runtime_files, options, talker_api)
tagger_window = TaggerWindow(config[0].runtime_files, config, talker_api)
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
tagger_window.show()

View File

@ -72,8 +72,8 @@ class IssueIdentifier:
result_one_good_match = 4
result_multiple_good_matches = 5
def __init__(self, comic_archive: ComicArchive, options: settngs.Namespace, talker_api: ComicTalker) -> None:
self.options = options
def __init__(self, comic_archive: ComicArchive, config: settngs.Namespace, talker_api: ComicTalker) -> None:
self.config = config
self.talker_api = talker_api
self.comic_archive: ComicArchive = comic_archive
self.image_hasher = 1
@ -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 = config.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 config.identifier_publisher_filter]
self.additional_metadata = GenericMetadata()
self.output_function: Callable[[str], None] = IssueIdentifier.default_write_output
@ -241,10 +241,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.config.filename_complicated_parser,
self.config.filename_remove_c2c,
self.config.filename_remove_fcbd,
self.config.filename_remove_publisher,
)
working_md = md_from_filename.copy()
@ -294,7 +294,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.config.runtime_config.user_cache_dir).fetch(
primary_img_url, blocking=True
)
except ImageFetcherException as e:
@ -316,7 +316,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.config.runtime_config.user_cache_dir).fetch(
alt_url, blocking=True
)
except ImageFetcherException as e:
@ -505,7 +505,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.options.identifier_border_crop_percent)
cropped_border = self.crop_border(cover_image_data, self.config.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")

View File

@ -42,7 +42,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
def __init__(
self,
parent: QtWidgets.QWidget,
options: settngs.Namespace,
config: settngs.Namespace,
talker_api: ComicTalker,
series_id: str,
issue_number: str,
@ -52,7 +52,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, config.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)
gridlayout.addWidget(self.coverWidget)
@ -71,18 +71,18 @@ class IssueSelectionWindow(QtWidgets.QDialog):
self.series_id = series_id
self.issue_id: str = ""
self.options = options
self.config = config
self.talker_api = talker_api
self.url_fetch_thread = None
self.issue_list: list[ComicIssue] = []
# Display talker logo and set url
self.lblIssuesSourceName.setText(talker_api.static_options.attribution_string)
self.lblIssuesSourceName.setText(talker_api.static_config.attribution_string)
self.imageIssuesSourceWidget = CoverImageWidget(
self.imageIssuesSourceLogo,
CoverImageWidget.URLMode,
options.runtime_config.user_cache_dir,
config.runtime_config.user_cache_dir,
talker_api,
False,
)

View File

@ -20,16 +20,14 @@ import json
import logging.handlers
import signal
import sys
from collections.abc import Mapping
import settngs
import comicapi
import comictalker.comictalkerapi as ct_api
from comictaggerlib import cli, ctoptions
from comictaggerlib import cli, ctsettings
from comictaggerlib.ctversion import version
from comictaggerlib.log import setup_logging
from comictalker.talkerbase import ComicTalker
if sys.version_info < (3, 10):
import importlib_metadata
@ -49,8 +47,8 @@ logger = logging.getLogger("comictagger")
logger.setLevel(logging.DEBUG)
def update_publishers(options: settngs.Namespace) -> None:
json_file = options.runtime_config.user_config_dir / "publishers.json"
def update_publishers(config: settngs.Namespace) -> None:
json_file = config.runtime_config.user_config_dir / "publishers.json"
if json_file.exists():
try:
comicapi.utils.update_publishers(json.loads(json_file.read_text("utf-8")))
@ -62,49 +60,48 @@ class App:
"""docstring for App"""
def __init__(self) -> None:
self.options = settngs.Config({}, {})
self.initial_arg_parser = ctoptions.initial_cmd_line_parser()
self.config = settngs.Config({}, {})
self.initial_arg_parser = ctsettings.initial_commandline_parser()
self.config_load_success = False
self.talker_plugins: Mapping[str, ComicTalker] = {}
def run(self) -> None:
opts = self.initialize()
self.initialize_dirs(opts.config)
self.load_plugins(opts)
self.register_options()
self.options = self.parse_options(opts.config)
conf = self.initialize()
self.initialize_dirs(conf.config)
self.load_plugins(conf)
self.register_settings()
self.config = self.parse_settings(conf.config)
self.main()
def load_plugins(self, opts) -> None:
comicapi.comicarchive.load_archive_plugins()
ctoptions.talker_plugins = ct_api.get_talkers(version, opts.config.user_cache_dir)
ctsettings.talkers = ct_api.get_talkers(version, opts.config.user_cache_dir)
def initialize(self) -> argparse.Namespace:
opts, _ = self.initial_arg_parser.parse_known_args()
assert opts is not None
setup_logging(opts.verbose, opts.config.user_log_dir)
return opts
conf, _ = self.initial_arg_parser.parse_known_args()
assert conf is not None
setup_logging(conf.verbose, conf.config.user_log_dir)
return conf
def register_options(self) -> None:
def register_settings(self) -> None:
self.manager = settngs.Manager(
"""A utility for reading and writing metadata to comic archives.\n\n\nIf no options are given, %(prog)s will run in windowed mode.""",
"For more help visit the wiki at: https://github.com/comictagger/comictagger/wiki",
)
ctoptions.register_commandline(self.manager)
ctoptions.register_settings(self.manager)
ctoptions.register_plugin_settings(self.manager)
ctsettings.register_commandline_settings(self.manager)
ctsettings.register_file_settings(self.manager)
ctsettings.register_plugin_settings(self.manager)
def parse_options(self, config_paths: ctoptions.ComicTaggerPaths) -> settngs.Config:
options, self.config_load_success = self.manager.parse_config(config_paths.user_config_dir / "settings.json")
options = self.manager.get_namespace(options)
def parse_settings(self, config_paths: ctsettings.ComicTaggerPaths) -> settngs.Config:
config, self.config_load_success = self.manager.parse_config(config_paths.user_config_dir / "settings.json")
config = self.manager.get_namespace(config)
options = ctoptions.validate_commandline_options(options, self.manager)
options = ctoptions.validate_settings(options)
options = ctoptions.validate_plugin_settings(options)
return options
config = ctsettings.validate_commandline_settings(config, self.manager)
config = ctsettings.validate_file_settings(config)
config = ctsettings.validate_plugin_settings(config)
return config
def initialize_dirs(self, paths: ctoptions.ComicTaggerPaths) -> None:
def initialize_dirs(self, paths: ctsettings.ComicTaggerPaths) -> None:
paths.user_data_dir.mkdir(parents=True, exist_ok=True)
paths.user_config_dir.mkdir(parents=True, exist_ok=True)
paths.user_cache_dir.mkdir(parents=True, exist_ok=True)
@ -117,8 +114,8 @@ class App:
logger.debug("user_log_dir: %s", paths.user_log_dir)
def main(self) -> None:
assert self.options is not None
# options already loaded
assert self.config is not None
# config already loaded
error = None
signal.signal(signal.SIGINT, signal.SIG_DFL)
@ -128,40 +125,48 @@ class App:
logger.debug("%s\t%s", pkg.metadata["Name"], pkg.metadata["Version"])
comicapi.utils.load_publishers()
update_publishers(self.options[0])
update_publishers(self.config[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.config[0].runtime_no_gui:
self.config[0].runtime_no_gui = True
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
if self.options[0].commands_only_set_cv_key:
# manage the CV API key
# None comparison is used so that the empty string can unset the value
if self.config[0].talker_comicvine_cv_api_key is not None or self.config[0].talker_comicvine_cv_url is not None:
settings_path = self.config[0].runtime_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_load_success:
print("Key set") # noqa: T201
return
try:
talker_api = self.talker_plugins[self.options[0].talkers_source]
talker_api = ctsettings.talkers[self.config[0].talkers_source]
except Exception as e:
logger.exception(f"Unable to load talker {self.options[0].talkers_source}")
# TODO error True can be changed to False after the talker settings menu generation is in
error = (f"Unable to load talker {self.options[0].talkers_source}: {e}", True)
logger.exception(f"Unable to load talker {self.config[0].talkers_source}")
error = (f"Unable to load talker {self.config[0].talkers_source}: {e}", True)
talker_api = None
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.config[0].runtime_config.user_log_dir}' for more details",
True,
)
if self.options[0].runtime_no_gui:
if self.config[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)
assert talker_api is not None
try:
cli.CLI(self.options[0], talker_api).run()
cli.CLI(self.config[0], talker_api).run()
except Exception:
logger.exception("CLI mode failed")
else:
gui.open_tagger_window(talker_api, self.options, error)
gui.open_tagger_window(talker_api, self.config, error)
def main():

View File

@ -37,7 +37,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
parent: QtWidgets.QWidget,
matches: list[IssueResult],
comic_archive: ComicArchive,
options: settngs.Values,
config: settngs.Values,
talker_api: ComicTalker,
) -> None:
super().__init__(parent)
@ -45,7 +45,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, config.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)
gridlayout.addWidget(self.altCoverWidget)

View File

@ -38,7 +38,7 @@ class RenameWindow(QtWidgets.QDialog):
parent: QtWidgets.QWidget,
comic_archive_list: list[ComicArchive],
data_style: int,
options: settngs.Config,
config: settngs.Config,
talker_api: ComicTalker,
) -> None:
super().__init__(parent)
@ -54,39 +54,39 @@ class RenameWindow(QtWidgets.QDialog):
)
)
self.options = options
self.config = config
self.talker_api = talker_api
self.comic_archive_list = comic_archive_list
self.data_style = data_style
self.rename_list: list[str] = []
self.btnSettings.clicked.connect(self.modify_settings)
platform = "universal" if self.options[0].rename_strict else "auto"
self.renamer = FileRenamer(None, platform=platform, replacements=self.options[0].rename_replacements)
platform = "universal" if self.config[0].rename_strict else "auto"
self.renamer = FileRenamer(None, platform=platform, replacements=self.config[0].rename_replacements)
self.do_preview()
def config_renamer(self, ca: ComicArchive, md: GenericMetadata | None = None) -> str:
self.renamer.set_template(self.options[0].rename_template)
self.renamer.set_issue_zero_padding(self.options[0].rename_issue_number_padding)
self.renamer.set_smart_cleanup(self.options[0].rename_use_smart_string_cleanup)
self.renamer.replacements = self.options[0].rename_replacements
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
new_ext = ca.path.suffix # default
if self.options[0].filename_rename_set_extension_based_on_archive:
if self.config[0].filename_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.options[0].filename_complicated_parser,
self.options[0].filename_remove_c2c,
self.options[0].filename_remove_fcbd,
self.options[0].filename_remove_publisher,
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.renamer.set_metadata(md)
self.renamer.move = self.options[0].rename_move_to_dir
self.renamer.move = self.config[0].rename_move_to_dir
return new_ext
def do_preview(self) -> None:
@ -99,7 +99,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].rename_template)
logger.exception("Invalid format string: %s", self.config[0].rename_template)
QtWidgets.QMessageBox.critical(
self,
"Invalid format string!",
@ -114,7 +114,7 @@ class RenameWindow(QtWidgets.QDialog):
except Exception as e:
logger.exception(
"Formatter failure: %s metadata: %s",
self.options[0].rename_template,
self.config[0].rename_template,
self.renamer.metadata,
)
QtWidgets.QMessageBox.critical(
@ -162,7 +162,7 @@ class RenameWindow(QtWidgets.QDialog):
self.twList.setSortingEnabled(True)
def modify_settings(self) -> None:
settingswin = SettingsWindow(self, self.options, self.talker_api)
settingswin = SettingsWindow(self, self.config, self.talker_api)
settingswin.setModal(True)
settingswin.show_rename_tab()
settingswin.exec()
@ -192,7 +192,7 @@ class RenameWindow(QtWidgets.QDialog):
folder = get_rename_dir(
comic[0],
self.options[0].rename_dir if self.options[0].rename_move_to_dir else None,
self.config[0].rename_dir if self.config[0].rename_move_to_dir else None,
)
full_path = folder / comic[1]

View File

@ -111,7 +111,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
issue_count: int,
cover_index_list: list[int],
comic_archive: ComicArchive | None,
options: settngs.Namespace,
config: settngs.Namespace,
talker_api: ComicTalker,
autoselect: bool = False,
literal: bool = False,
@ -121,7 +121,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
uic.loadUi(ui_path / "seriesselectionwindow.ui", self)
self.imageWidget = CoverImageWidget(
self.imageContainer, CoverImageWidget.URLMode, options.runtime_config.user_cache_dir, talker_api
self.imageContainer, CoverImageWidget.URLMode, config.runtime_config.user_cache_dir, talker_api
)
gridlayout = QtWidgets.QGridLayout(self.imageContainer)
gridlayout.addWidget(self.imageWidget)
@ -138,7 +138,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
)
)
self.options = options
self.config = config
self.series_name = series_name
self.issue_number = issue_number
self.issue_id: str = ""
@ -156,18 +156,18 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
self.progdialog: QtWidgets.QProgressDialog | None = None
self.search_thread: SearchThread | None = None
self.use_filter = self.options.talkers_always_use_publisher_filter
self.use_filter = self.config.talkers_always_use_publisher_filter
# Load to retrieve settings
self.talker_api = talker_api
# Display talker logo and set url
self.lblSourceName.setText(talker_api.static_options.attribution_string)
self.lblSourceName.setText(talker_api.static_config.attribution_string)
self.imageSourceWidget = CoverImageWidget(
self.imageSourceLogo,
CoverImageWidget.URLMode,
options.runtime_config.user_cache_dir,
config.runtime_config.user_cache_dir,
talker_api,
False,
)
@ -224,7 +224,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
self.iddialog.rejected.connect(self.identify_cancel)
self.iddialog.show()
self.ii = IssueIdentifier(self.comic_archive, self.options, self.talker_api)
self.ii = IssueIdentifier(self.comic_archive, self.config, self.talker_api)
md = GenericMetadata()
md.series = self.series_name
@ -298,7 +298,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
if choices:
selector = MatchSelectionWindow(
self, matches, self.comic_archive, talker_api=self.talker_api, options=self.options
self, matches, self.comic_archive, talker_api=self.talker_api, config=self.config
)
selector.setModal(True)
selector.exec()
@ -315,7 +315,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
self.show_issues()
def show_issues(self) -> None:
selector = IssueSelectionWindow(self, self.options, self.talker_api, self.series_id, self.issue_number)
selector = IssueSelectionWindow(self, self.config, self.talker_api, self.series_id, self.issue_number)
title = ""
for record in self.ct_search_results:
if record.id == self.series_id:
@ -343,7 +343,7 @@ class SeriesSelectionWindow(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.talkers_series_match_search_thresh
self.talker_api, self.series_name, refresh, self.literal, self.config.talkers_series_match_search_thresh
)
self.search_thread.searchComplete.connect(self.search_complete)
self.search_thread.progressUpdate.connect(self.search_progress_update)
@ -395,7 +395,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.options.identifier_publisher_filter}
publisher_filter = {s.strip().casefold() for s in self.config.identifier_publisher_filter}
# use '' as publisher name if None
self.ct_search_results = list(
filter(
@ -410,7 +410,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.options.talkers_sort_series_by_year:
if self.config.talkers_sort_series_by_year:
try:
self.ct_search_results = sorted(
self.ct_search_results,
@ -428,7 +428,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog):
logger.exception("bad data error sorting results by count_of_issues")
# move sanitized matches to the front
if self.options.talkers_exact_series_matches_first:
if self.config.talkers_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

@ -130,7 +130,7 @@ Spider-Geddon #1 - New Players; Check In
class SettingsWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, options: settngs.Config, talker_api: ComicTalker) -> None:
def __init__(self, parent: QtWidgets.QWidget, config: settngs.Config, talker_api: ComicTalker) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "settingswindow.ui", self)
@ -139,7 +139,7 @@ class SettingsWindow(QtWidgets.QDialog):
QtCore.Qt.WindowType(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
)
self.options = options
self.config = config
self.talker_api = talker_api
self.name = "Settings"
@ -269,53 +269,53 @@ class SettingsWindow(QtWidgets.QDialog):
def settings_to_form(self) -> None:
# Copy values from settings to form
if "archiver" in self.options[1] and "rar" in self.options[1]["archiver"]:
self.leRarExePath.setText(getattr(self.options[0], self.options[1]["archiver"]["rar"].internal_name))
if "archiver" in self.config[1] and "rar" in self.config[1]["archiver"]:
self.leRarExePath.setText(getattr(self.config[0], self.config[1]["archiver"]["rar"].internal_name))
else:
self.leRarExePath.setEnabled(False)
self.sbNameMatchIdentifyThresh.setValue(self.options[0].identifier_series_match_identify_thresh)
self.sbNameMatchSearchThresh.setValue(self.options[0].talkers_series_match_search_thresh)
self.tePublisherFilter.setPlainText("\n".join(self.options[0].identifier_publisher_filter))
self.sbNameMatchIdentifyThresh.setValue(self.config[0].identifier_series_match_identify_thresh)
self.sbNameMatchSearchThresh.setValue(self.config[0].talkers_series_match_search_thresh)
self.tePublisherFilter.setPlainText("\n".join(self.config[0].identifier_publisher_filter))
self.cbxCheckForNewVersion.setChecked(self.options[0].general_check_for_new_version)
self.cbxCheckForNewVersion.setChecked(self.config[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.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.switch_parser()
self.cbxUseSeriesStartAsVolume.setChecked(self.options[0].talker_comicvine_cv_use_series_start_as_volume)
self.cbxClearFormBeforePopulating.setChecked(self.options[0].talkers_clear_form_before_populating)
self.cbxRemoveHtmlTables.setChecked(self.options[0].talker_comicvine_cv_remove_html_tables)
self.cbxUseSeriesStartAsVolume.setChecked(self.config[0].talker_comicvine_cv_use_series_start_as_volume)
self.cbxClearFormBeforePopulating.setChecked(self.config[0].talkers_clear_form_before_populating)
self.cbxRemoveHtmlTables.setChecked(self.config[0].talker_comicvine_cv_remove_html_tables)
self.cbxUseFilter.setChecked(self.options[0].talkers_always_use_publisher_filter)
self.cbxSortByYear.setChecked(self.options[0].talkers_sort_series_by_year)
self.cbxExactMatches.setChecked(self.options[0].talkers_exact_series_matches_first)
self.cbxUseFilter.setChecked(self.config[0].talkers_always_use_publisher_filter)
self.cbxSortByYear.setChecked(self.config[0].talkers_sort_series_by_year)
self.cbxExactMatches.setChecked(self.config[0].talkers_exact_series_matches_first)
self.leKey.setText(self.options[0].talker_comicvine_cv_api_key)
self.leURL.setText(self.options[0].talker_comicvine_cv_url)
self.leKey.setText(self.config[0].talker_comicvine_cv_api_key)
self.leURL.setText(self.config[0].talker_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_transform_on_import)
self.cbxApplyCBLTransformOnBatchOperation.setChecked(self.options[0].cbl_apply_transform_on_bulk_operation)
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.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.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)
for table, replacments in zip(
(self.twLiteralReplacements, self.twValueReplacements), self.options[0].rename_replacements
(self.twLiteralReplacements, self.twValueReplacements), self.config[0].rename_replacements
):
table.clearContents()
for i in reversed(range(table.rowCount())):
@ -350,7 +350,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.config[0].rename_template)
QtWidgets.QMessageBox.critical(
self,
"Invalid format string!",
@ -364,7 +364,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.config[0].rename_template, self.renamer.metadata
)
QtWidgets.QMessageBox.critical(
self,
@ -377,75 +377,75 @@ class SettingsWindow(QtWidgets.QDialog):
)
# Copy values from form to settings and save
if "archiver" in self.options[1] and "rar" in self.options[1]["archiver"]:
setattr(self.options[0], self.options[1]["archiver"]["rar"].internal_name, str(self.leRarExePath.text()))
if "archiver" in self.config[1] and "rar" in self.config[1]["archiver"]:
setattr(self.config[0], self.config[1]["archiver"]["rar"].internal_name, str(self.leRarExePath.text()))
# make sure rar program is now in the path for the rar class
if self.options[0].archivers_rar:
if self.config[0].archivers_rar:
utils.add_to_path(os.path.dirname(str(self.leRarExePath.text())))
if not str(self.leIssueNumPadding.text()).isdigit():
self.leIssueNumPadding.setText("0")
self.options[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked()
self.config[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked()
self.options[0].identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
self.options[0].talkers_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
self.options[0].identifier_publisher_filter = [
self.config[0].identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value()
self.config[0].talkers_series_match_search_thresh = self.sbNameMatchSearchThresh.value()
self.config[0].identifier_publisher_filter = [
x.strip() for x in str(self.tePublisherFilter.toPlainText()).splitlines() if x.strip()
]
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.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.options[0].talker_comicvine_cv_use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
self.options[0].talkers_clear_form_before_populating = self.cbxClearFormBeforePopulating.isChecked()
self.options[0].talker_comicvine_cv_remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
self.config[0].talker_comicvine_cv_use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
self.config[0].talkers_clear_form_before_populating = self.cbxClearFormBeforePopulating.isChecked()
self.config[0].talker_comicvine_cv_remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
self.options[0].talkers_always_use_publisher_filter = self.cbxUseFilter.isChecked()
self.options[0].talkers_sort_series_by_year = self.cbxSortByYear.isChecked()
self.options[0].talkers_exact_series_matches_first = self.cbxExactMatches.isChecked()
self.config[0].talkers_always_use_publisher_filter = self.cbxUseFilter.isChecked()
self.config[0].talkers_sort_series_by_year = self.cbxSortByYear.isChecked()
self.config[0].talkers_exact_series_matches_first = self.cbxExactMatches.isChecked()
if self.leKey.text().strip():
self.options[0].talker_comicvine_cv_api_key = self.leKey.text().strip()
self.talker_api.api_key = self.options[0].talker_comicvine_cv_api_key
self.config[0].talker_comicvine_cv_api_key = self.leKey.text().strip()
self.talker_api.api_key = self.config[0].talker_comicvine_cv_api_key
if self.leURL.text().strip():
self.options[0].talker_comicvine_cv_url = self.leURL.text().strip()
self.talker_api.api_url = self.options[0].talker_comicvine_cv_url
self.config[0].talker_comicvine_cv_url = self.leURL.text().strip()
self.talker_api.api_url = self.config[0].talker_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_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
self.options[0].cbl_apply_transform_on_bulk_operation = self.cbxApplyCBLTransformOnBatchOperation.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.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.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.options[0].rename_strict = self.cbxRenameStrict.isChecked()
self.options[0].rename_replacements = self.get_replacemnts()
self.config[0].rename_strict = self.cbxRenameStrict.isChecked()
self.config[0].rename_replacements = self.get_replacemnts()
settngs.save_file(self.options, self.options[0].runtime_config.user_config_dir / "settings.json")
self.parent().options = self.options
settngs.save_file(self.config, self.config[0].runtime_config.user_config_dir / "settings.json")
self.parent().config = self.config
QtWidgets.QDialog.accept(self)
def select_rar(self) -> None:
self.select_file(self.leRarExePath, "RAR")
def clear_cache(self) -> None:
ImageFetcher(self.options[0].runtime_config.user_cache_dir).clear_cache()
ComicCacher(self.options[0].runtime_config.user_cache_dir, version).clear_cache()
ImageFetcher(self.config[0].runtime_config.user_cache_dir).clear_cache()
ComicCacher(self.config[0].runtime_config.user_cache_dir, version).clear_cache()
QtWidgets.QMessageBox.information(self, self.name, "Cache has been cleared.")
def test_api_key(self) -> None:
@ -455,7 +455,7 @@ class SettingsWindow(QtWidgets.QDialog):
QtWidgets.QMessageBox.warning(self, "API Key Test", "Key is NOT valid.")
def reset_settings(self) -> None:
self.options = settngs.Config(settngs.defaults(self.options[1]), self.options[1])
self.config = settngs.Config(settngs.defaults(self.config[1]), self.config[1])
self.settings_to_form()
QtWidgets.QMessageBox.information(self, self.name, self.name + " have been returned to default values.")

View File

@ -79,24 +79,24 @@ class TaggerWindow(QtWidgets.QMainWindow):
def __init__(
self,
file_list: list[str],
options: settngs.Config,
config: settngs.Config,
talker_api: ComicTalker,
parent: QtWidgets.QWidget | None = None,
) -> None:
super().__init__(parent)
uic.loadUi(ui_path / "taggerwindow.ui", self)
self.options = options
self.config = config
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(config[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", config[0].internal_install_id)
# send file list to other instance
if file_list:
socket.write(pickle.dumps(file_list))
@ -108,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(config[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(config[0].internal_install_id)
ok = self.socketServer.listen(config[0].internal_install_id)
if not ok:
logger.error(
"Cannot start local socket with key [%s]. Reason: %s",
options[0].internal_install_id,
config[0].internal_install_id,
self.socketServer.errorString(),
)
sys.exit()
@ -130,14 +130,14 @@ class TaggerWindow(QtWidgets.QMainWindow):
grid_layout = QtWidgets.QGridLayout(self.tabPages)
grid_layout.addWidget(self.page_list_editor)
self.fileSelectionList = FileSelectionList(self.widgetListHolder, self.options[0], self.dirty_flag_verification)
self.fileSelectionList = FileSelectionList(self.widgetListHolder, self.config[0], self.dirty_flag_verification)
grid_layout = QtWidgets.QGridLayout(self.widgetListHolder)
grid_layout.addWidget(self.fileSelectionList)
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_sort_column, QtCore.Qt.SortOrder(self.options[0].internal_sort_direction)
self.config[0].internal_sort_column, QtCore.Qt.SortOrder(self.config[0].internal_sort_direction)
)
# we can't specify relative font sizes in the UI designer, so
@ -155,13 +155,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 config[0].runtime_type and isinstance(config[0].runtime_type[0], int):
# respect the command line option tag type
options[0].internal_save_data_style = options[0].runtime_type[0]
options[0].internal_load_data_style = options[0].runtime_type[0]
config[0].internal_save_data_style = config[0].runtime_type[0]
config[0].internal_load_data_style = config[0].runtime_type[0]
self.save_data_style = options[0].internal_save_data_style
self.load_data_style = options[0].internal_load_data_style
self.save_data_style = config[0].internal_save_data_style
self.load_data_style = config[0].internal_load_data_style
self.setAcceptDrops(True)
self.config_menus()
@ -226,8 +226,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.show()
self.set_app_position()
if self.options[0].internal_form_width != -1:
self.splitter.setSizes([self.options[0].internal_form_width, self.options[0].internal_list_width])
if self.config[0].internal_form_width != -1:
self.splitter.setSizes([self.config[0].internal_form_width, self.config[0].internal_list_width])
self.raise_()
QtCore.QCoreApplication.processEvents()
self.resizeEvent(None)
@ -244,7 +244,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.config[0].dialog_show_disclaimer:
checked = OptionalMessageDialog.msg(
self,
"Welcome!",
@ -259,9 +259,9 @@ use ComicTagger on local copies of your comics.<br><br>
Have fun!
""",
)
self.options[0].dialog_show_disclaimer = not checked
self.config[0].dialog_show_disclaimer = not checked
if self.options[0].general_check_for_new_version:
if self.config[0].general_check_for_new_version:
self.check_latest_version_online()
def open_file_event(self, url: QtCore.QUrl) -> None:
@ -274,7 +274,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.config[0].runtime_config.user_log_dir / "ComicTagger.log").read_text("utf-8")
except Exception:
current_logs = ""
root_logger = logging.getLogger()
@ -479,7 +479,7 @@ Have fun!
f"You have selected {non_zip_count} archive(s) to export to Zip format. "
""" New archives will be created in the same folder as the original.
Please choose options below, and select OK.
Please choose config below, and select OK.
"""
),
)
@ -607,10 +607,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.config[0].filename_complicated_parser,
self.config[0].filename_remove_c2c,
self.config[0].filename_remove_fcbd,
self.config[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())
@ -969,10 +969,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.config[0].filename_complicated_parser,
self.config[0].filename_remove_c2c,
self.config[0].filename_remove_fcbd,
self.config[0].filename_remove_publisher,
split_words,
)
if new_metadata is not None:
@ -993,8 +993,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.config[0].internal_last_opened_folder is not None:
dialog.setDirectory(self.config[0].internal_last_opened_folder)
if not folder_mode:
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar *.cb7 *.7z)"
@ -1044,7 +1044,7 @@ Have fun!
issue_count,
cover_index_list,
self.comic_archive,
self.options[0],
self.config[0],
self.talker_api,
autoselect,
literal,
@ -1072,10 +1072,10 @@ Have fun!
else:
QtWidgets.QApplication.restoreOverrideCursor()
if new_metadata is not None:
if self.options[0].cbl_apply_transform_on_import:
new_metadata = CBLTransformer(new_metadata, self.options[0]).apply()
if self.config[0].cbl_apply_transform_on_import:
new_metadata = CBLTransformer(new_metadata, self.config[0]).apply()
if self.options[0].talkers_clear_form_before_populating:
if self.config[0].talkers_clear_form_before_populating:
self.clear_form()
notes = (
@ -1130,7 +1130,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_load_data_style = self.load_data_style
self.config[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)
@ -1141,7 +1141,7 @@ Have fun!
def set_save_data_style(self, s: int) -> None:
self.save_data_style = self.cbSaveDataStyle.itemData(s)
self.options[0].internal_save_data_style = self.save_data_style
self.config[0].internal_save_data_style = self.save_data_style
self.update_style_tweaks()
self.update_menus()
@ -1360,15 +1360,15 @@ Have fun!
def show_settings(self) -> None:
settingswin = SettingsWindow(self, self.options, self.talker_api)
settingswin = SettingsWindow(self, self.config, self.talker_api)
settingswin.setModal(True)
settingswin.exec()
settingswin.result()
def set_app_position(self) -> None:
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)
if self.config[0].internal_window_width != 0:
self.move(self.config[0].internal_window_x, self.config[0].internal_window_y)
self.resize(self.config[0].internal_window_width, self.config[0].internal_window_height)
else:
screen = QtGui.QGuiApplication.primaryScreen().geometry()
size = self.frameGeometry()
@ -1635,8 +1635,8 @@ 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_transform_on_bulk_operation:
md = CBLTransformer(md, self.options[0]).apply()
if dest_style == MetaDataStyle.CBI and self.config[0].cbl_apply_transform_on_bulk_operation:
md = CBLTransformer(md, self.config[0]).apply()
if not ca.write_metadata(md, dest_style):
failed_list.append(ca.path)
@ -1674,8 +1674,8 @@ Have fun!
logger.exception("Save aborted.")
if not ct_md.is_empty:
if self.options[0].cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.options[0]).apply()
if self.config[0].cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.config[0]).apply()
QtWidgets.QApplication.restoreOverrideCursor()
@ -1694,7 +1694,7 @@ Have fun!
self, ca: ComicArchive, match_results: OnlineMatchResults, dlg: AutoTagStartWindow
) -> tuple[bool, OnlineMatchResults]:
success = False
ii = IssueIdentifier(ca, self.options[0], self.talker_api)
ii = IssueIdentifier(ca, self.config[0], self.talker_api)
# read in metadata, and parse file name if not there
try:
@ -1704,10 +1704,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.config[0].filename_complicated_parser,
self.config[0].filename_remove_c2c,
self.config[0].filename_remove_fcbd,
self.config[0].filename_remove_publisher,
dlg.split_words,
)
if dlg.ignore_leading_digits_in_filename and md.series is not None:
@ -1793,7 +1793,7 @@ Have fun!
)
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
if self.options[0].talkers_auto_imprint:
if self.config[0].talkers_auto_imprint:
md.fix_publisher()
if not ca.write_metadata(md, self.save_data_style):
@ -1822,10 +1822,10 @@ Have fun!
atstartdlg = AutoTagStartWindow(
self,
self.options[0],
self.config[0],
(
f"You have selected {len(ca_list)} archive(s) to automatically identify and write {MetaDataStyle.name[style]} tags to."
"\n\nPlease choose options below, and select OK to Auto-Tag."
"\n\nPlease choose config below, and select OK to Auto-Tag."
),
)
@ -1925,7 +1925,7 @@ Have fun!
match_results.multiple_matches,
style,
self.actual_issue_data_fetch,
self.options[0],
self.config[0],
self.talker_api,
)
matchdlg.setModal(True)
@ -1971,17 +1971,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_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.config[0].internal_window_width = appsize.width()
self.config[0].internal_window_height = appsize.height()
self.config[0].internal_window_x = self.x()
self.config[0].internal_window_y = self.y()
self.config[0].internal_form_width = self.splitter.sizes()[0]
self.config[0].internal_list_width = self.splitter.sizes()[1]
(
self.options[0].internal_sort_column,
self.options[0].internal_sort_direction,
self.config[0].internal_sort_column,
self.config[0].internal_sort_direction,
) = self.fileSelectionList.get_sorting()
settngs.save_file(self.options, self.options[0].runtime_config.user_config_dir / "settings.json")
settngs.save_file(self.config, self.config[0].runtime_config.user_config_dir / "settings.json")
event.accept()
else:
@ -2030,7 +2030,7 @@ Have fun!
def apply_cbl_transform(self) -> None:
self.form_to_metadata()
self.metadata = CBLTransformer(self.metadata, self.options[0]).apply()
self.metadata = CBLTransformer(self.metadata, self.config[0]).apply()
self.metadata_to_form()
def recalc_page_dimensions(self) -> None:
@ -2056,7 +2056,7 @@ Have fun!
"File Rename", "If you rename files now, unsaved data in the form will be lost. Are you sure?"
):
dlg = RenameWindow(self, ca_list, self.load_data_style, self.options, self.talker_api)
dlg = RenameWindow(self, ca_list, self.load_data_style, self.config, self.talker_api)
dlg.setModal(True)
if dlg.exec() and self.comic_archive is not None:
self.fileSelectionList.update_selected_rows()
@ -2075,7 +2075,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.config[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)
@ -2106,10 +2106,10 @@ Have fun!
def check_latest_version_online(self) -> None:
version_checker = VersionChecker()
self.version_check_complete(version_checker.get_latest_version(self.options[0].internal_install_id))
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.options[0].dialog_dont_notify_about_this_version):
if new_version[0] not in (self.version, self.config[0].dialog_dont_notify_about_this_version):
website = "https://github.com/comictagger/comictagger"
checked = OptionalMessageDialog.msg(
self,
@ -2120,7 +2120,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.config[0].dialog_dont_notify_about_this_version = new_version[0]
def on_incoming_socket_connection(self) -> None:
# Accept connection from other instance.

View File

@ -37,7 +37,7 @@ class SourceDetails:
self.logo = logo
class SourceStaticOptions:
class SourceStaticSettings:
def __init__(
self,
website: str = "",
@ -143,7 +143,7 @@ class ComicTalker:
def __init__(self, version: str, cache_folder: pathlib.Path) -> None:
# Identity name for the information source etc.
self.source_details = SourceDetails()
self.static_options = SourceStaticOptions()
self.static_config = SourceStaticSettings()
self.cache_folder = cache_folder
self.version = version
self.api_key: str = ""

View File

@ -34,7 +34,7 @@ from comicapi.genericmetadata import GenericMetadata
from comicapi.issuestring import IssueString
from comictalker.comiccacher import ComicCacher
from comictalker.resulttypes import ComicIssue, ComicSeries, Credit
from comictalker.talkerbase import ComicTalker, SourceDetails, SourceStaticOptions, TalkerDataError, TalkerNetworkError
from comictalker.talkerbase import ComicTalker, SourceDetails, SourceStaticSettings, TalkerDataError, TalkerNetworkError
logger = logging.getLogger(__name__)
@ -165,7 +165,7 @@ class ComicVineTalker(ComicTalker):
ident="comicvine",
logo="https://comicvine.gamespot.com/a/bundles/comicvinesite/images/logo.png",
)
self.static_options = SourceStaticOptions(
self.static_config = SourceStaticSettings(
website="https://comicvine.gamespot.com/",
attribution_string="Metadata provided by <a href='https://comicvine.gamespot.com/'>Comic Vine</a>",
has_issues=True,

View File

@ -123,7 +123,7 @@ def test_invalid_zip(tmp_comic):
archivers = [
pytest.param(x.load(), marks=pytest.mark.xfail(not (x.load().enabled), reason="archiver not enabled"))
for x in entry_points(group="comicapi_archivers")
for x in entry_points(group="comicapi.archiver")
]

View File

@ -6,10 +6,10 @@ import comictalker.comiccacher
from testing.comicdata import search_results
def test_create_cache(options, mock_version):
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_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()
def test_search_results(comic_cache):
@ -25,9 +25,5 @@ def test_search_results(comic_cache):
def test_series_info(comic_cache, series_info):
comic_cache.add_series_info(series_record=series_info, source_name="test")
vi = series_info.copy()
# del vi["description"]
# del vi["image_url"]
cache_result = comic_cache.get_series_info(series_id=series_info.id, source_name="test")
# del cache_result["description"]
# del cache_result["image_url"]
assert vi == cache_result

View File

@ -14,7 +14,7 @@ from PIL import Image
import comicapi.comicarchive
import comicapi.genericmetadata
import comictaggerlib.ctoptions
import comictaggerlib.ctsettings
import comictalker.comiccacher
import comictalker.comictalkerapi
import comictalker.talkers.comicvine
@ -56,9 +56,7 @@ def no_requests(monkeypatch) -> None:
@pytest.fixture
def comicvine_api(
monkeypatch, cbz, comic_cache, mock_version, options
) -> comictalker.talkers.comicvine.ComicVineTalker:
def comicvine_api(monkeypatch, cbz, comic_cache, mock_version, config) -> comictalker.talkers.comicvine.ComicVineTalker:
# Any arguments may be passed and mock_get() will always return our
# mocked object, which only has the .json() method or None for invalid urls.
@ -118,7 +116,7 @@ def comicvine_api(
cv = comictalker.talkers.comicvine.ComicVineTalker(
version=mock_version[0],
cache_folder=options[0].runtime_config.user_cache_dir,
cache_folder=config[0].runtime_config.user_cache_dir,
)
return cv
@ -158,12 +156,12 @@ def seed_all_publishers(monkeypatch):
@pytest.fixture
def options(settings_manager, tmp_path):
def config(settings_manager, tmp_path):
comictaggerlib.ctoptions.register_commandline(settings_manager)
comictaggerlib.ctoptions.register_settings(settings_manager)
comictaggerlib.ctsettings.register_commandline_settings(settings_manager)
comictaggerlib.ctsettings.register_file_settings(settings_manager)
defaults = settings_manager.get_namespace(settings_manager.defaults())
defaults[0].runtime_config = comictaggerlib.ctoptions.ComicTaggerPaths(tmp_path / "config")
defaults[0].runtime_config = 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)
@ -179,5 +177,5 @@ def settings_manager():
@pytest.fixture
def comic_cache(options, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
yield comictalker.comiccacher.ComicCacher(options[0].runtime_config.user_cache_dir, mock_version[0])
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])

View File

@ -12,9 +12,9 @@ import testing.comicdata
import testing.comicvine
def test_crop(cbz_double_cover, options, tmp_path, comicvine_api):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, settings, comicvine_api)
def test_crop(cbz_double_cover, config, tmp_path, comicvine_api):
config, definitions = config
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, config, comicvine_api)
cropped = ii.crop_cover(cbz_double_cover.archiver.read_file("double_cover.jpg"))
original_cover = cbz_double_cover.get_page(0)
@ -25,17 +25,17 @@ 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):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
def test_get_search_keys(cbz, config, additional_md, expected, comicvine_api):
config, definitions = config
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, config, 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):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
def test_get_issue_cover_match_score(cbz, config, comicvine_api):
config, definitions = config
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, config, comicvine_api)
score = ii.get_issue_cover_match_score(
int(
comicapi.issuestring.IssueString(
@ -54,9 +54,9 @@ def test_get_issue_cover_match_score(cbz, options, comicvine_api):
assert expected == score
def test_search(cbz, options, comicvine_api):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
def test_search(cbz, config, comicvine_api):
config, definitions = config
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, config, comicvine_api)
results = ii.search()
cv_expected = {
"series": f"{testing.comicvine.cv_volume_result['results']['name']} ({testing.comicvine.cv_volume_result['results']['start_year']})",
@ -78,9 +78,9 @@ def test_search(cbz, options, comicvine_api):
assert r == e
def test_crop_border(cbz, options, comicvine_api):
settings, definitions = options
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
def test_crop_border(cbz, config, comicvine_api):
config, definitions = config
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, config, comicvine_api)
# This creates a white square centered on a black background
bg = Image.new("RGBA", (100, 100), (0, 0, 0, 255))