Split settings out into a separate package
This commit is contained in:
parent
eca421e0f2
commit
103379e548
@ -19,11 +19,11 @@ import logging
|
||||
import os
|
||||
from typing import Callable
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from comicapi.comicarchive import MetaDataStyle
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.resulttypes import IssueResult, MultipleMatch
|
||||
from comictaggerlib.ui import ui_path
|
||||
@ -42,7 +42,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
|
||||
match_set_list: list[MultipleMatch],
|
||||
style: int,
|
||||
fetch_func: Callable[[IssueResult], GenericMetadata],
|
||||
options: settings.OptionValues,
|
||||
options: settngs.ConfigValues,
|
||||
talker_api: ComicTalker,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -17,16 +17,16 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtWidgets, uic
|
||||
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.ui import ui_path
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AutoTagStartWindow(QtWidgets.QDialog):
|
||||
def __init__(self, parent: QtWidgets.QWidget, options: settings.OptionValues, msg: str) -> None:
|
||||
def __init__(self, parent: QtWidgets.QWidget, options: settngs.ConfigValues, msg: str) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
uic.loadUi(ui_path / "autotagstartwindow.ui", self)
|
||||
|
@ -17,14 +17,15 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import settngs
|
||||
|
||||
from comicapi.genericmetadata import CreditMetadata, GenericMetadata
|
||||
from comictaggerlib import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CBLTransformer:
|
||||
def __init__(self, metadata: GenericMetadata, options: settings.OptionValues) -> None:
|
||||
def __init__(self, metadata: GenericMetadata, options: settngs.ConfigValues) -> None:
|
||||
self.metadata = metadata
|
||||
self.options = options
|
||||
|
||||
|
@ -23,10 +23,12 @@ import sys
|
||||
from datetime import datetime
|
||||
from pprint import pprint
|
||||
|
||||
import settngs
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import ComicArchive, MetaDataStyle
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib import ctversion, settings
|
||||
from comictaggerlib import ctversion
|
||||
from comictaggerlib.cbltransformer import CBLTransformer
|
||||
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
|
||||
from comictaggerlib.graphics import graphics_path
|
||||
@ -38,7 +40,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CLI:
|
||||
def __init__(self, options: settings.OptionValues, talker_api: ComicTalker):
|
||||
def __init__(self, options: settngs.Values, talker_api: ComicTalker):
|
||||
self.options = options
|
||||
self.talker_api = talker_api
|
||||
self.batch_mode = False
|
||||
|
14
comictaggerlib/ctoptions/__init__.py
Normal file
14
comictaggerlib/ctoptions/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
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.types import ComicTaggerPaths
|
||||
|
||||
__all__ = [
|
||||
"initial_cmd_line_parser",
|
||||
"register_commandline",
|
||||
"register_settings",
|
||||
"validate_commandline_options",
|
||||
"validate_settings",
|
||||
"ComicTaggerPaths",
|
||||
]
|
@ -19,13 +19,13 @@ import argparse
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
from typing import Any
|
||||
|
||||
import settngs
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib import ctversion
|
||||
from comictaggerlib.settings.manager import Manager
|
||||
from comictaggerlib.settings.types import ComicTaggerPaths, metadata_type, parse_metadata_from_string
|
||||
from comictaggerlib.ctoptions.types import ComicTaggerPaths, metadata_type, parse_metadata_from_string
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -49,7 +49,7 @@ def initial_cmd_line_parser() -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def register_options(parser: Manager) -> None:
|
||||
def register_options(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",
|
||||
@ -200,7 +200,7 @@ def register_options(parser: Manager) -> None:
|
||||
parser.add_setting("files", nargs="*", file=False)
|
||||
|
||||
|
||||
def register_commands(parser: Manager) -> None:
|
||||
def register_commands(parser: settngs.Manager) -> None:
|
||||
parser.add_setting(
|
||||
"--version",
|
||||
action="store_true",
|
||||
@ -259,12 +259,12 @@ def register_commands(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def register_commandline(parser: Manager) -> None:
|
||||
def register_commandline(parser: settngs.Manager) -> None:
|
||||
parser.add_group("commands", register_commands, True)
|
||||
parser.add_group("runtime", register_options)
|
||||
|
||||
|
||||
def validate_commandline_options(options: dict[str, dict[str, Any]], parser: Manager) -> dict[str, dict[str, Any]]:
|
||||
def validate_commandline_options(options: settngs.Values, parser: settngs.Manager) -> settngs.Values:
|
||||
|
||||
if options["commands"]["version"]:
|
||||
parser.exit(
|
@ -4,12 +4,13 @@ import argparse
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
import settngs
|
||||
|
||||
from comictaggerlib.ctoptions.types import AppendAction
|
||||
from comictaggerlib.defaults import DEFAULT_REPLACEMENTS, Replacement, Replacements
|
||||
from comictaggerlib.settings.manager import Manager
|
||||
from comictaggerlib.settings.types import AppendAction
|
||||
|
||||
|
||||
def general(parser: Manager) -> None:
|
||||
def general(parser: settngs.Manager) -> None:
|
||||
# General Settings
|
||||
parser.add_setting("--rar-exe-path", default="rar", help="The path to the rar program")
|
||||
parser.add_setting(
|
||||
@ -22,7 +23,7 @@ def general(parser: Manager) -> None:
|
||||
parser.add_setting("send_usage_stats", default=False, cmdline=False)
|
||||
|
||||
|
||||
def internal(parser: Manager) -> None:
|
||||
def internal(parser: settngs.Manager) -> None:
|
||||
# automatic settings
|
||||
parser.add_setting("install_id", default=uuid.uuid4().hex, cmdline=False)
|
||||
parser.add_setting("last_selected_save_data_style", default=0, cmdline=False)
|
||||
@ -38,7 +39,7 @@ def internal(parser: Manager) -> None:
|
||||
parser.add_setting("last_filelist_sorted_order", default=0, cmdline=False)
|
||||
|
||||
|
||||
def identifier(parser: Manager) -> None:
|
||||
def identifier(parser: settngs.Manager) -> None:
|
||||
# identifier settings
|
||||
parser.add_setting("--series-match-identify-thresh", default=91, type=int, help="")
|
||||
parser.add_setting(
|
||||
@ -49,7 +50,7 @@ def identifier(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def dialog(parser: Manager) -> None:
|
||||
def dialog(parser: settngs.Manager) -> None:
|
||||
# Show/ask dialog flags
|
||||
parser.add_setting("ask_about_cbi_in_rar", default=True, cmdline=False)
|
||||
parser.add_setting("show_disclaimer", default=True, cmdline=False)
|
||||
@ -57,7 +58,7 @@ def dialog(parser: Manager) -> None:
|
||||
parser.add_setting("ask_about_usage_stats", default=True, cmdline=False)
|
||||
|
||||
|
||||
def filename(parser: Manager) -> None:
|
||||
def filename(parser: settngs.Manager) -> None:
|
||||
# filename parsing settings
|
||||
parser.add_setting(
|
||||
"--complicated-parser",
|
||||
@ -85,7 +86,7 @@ def filename(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def comicvine(parser: Manager) -> None:
|
||||
def comicvine(parser: settngs.Manager) -> None:
|
||||
# Comic Vine settings
|
||||
parser.add_setting(
|
||||
"--series-match-search-thresh",
|
||||
@ -145,7 +146,7 @@ def comicvine(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def cbl(parser: Manager) -> None:
|
||||
def cbl(parser: settngs.Manager) -> None:
|
||||
# CBL Transform settings
|
||||
parser.add_setting("--assume-lone-credit-is-primary", default=False, action=argparse.BooleanOptionalAction)
|
||||
parser.add_setting("--copy-characters-to-tags", default=False, action=argparse.BooleanOptionalAction)
|
||||
@ -158,7 +159,7 @@ def cbl(parser: Manager) -> None:
|
||||
parser.add_setting("--apply-cbl-transform-on-bulk-operation", default=False, action=argparse.BooleanOptionalAction)
|
||||
|
||||
|
||||
def rename(parser: Manager) -> None:
|
||||
def rename(parser: settngs.Manager) -> None:
|
||||
# Rename settings
|
||||
parser.add_setting("--template", default="{series} #{issue} ({year})", help="The teplate to use when renaming")
|
||||
parser.add_setting(
|
||||
@ -199,7 +200,7 @@ def rename(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def autotag(parser: Manager) -> None:
|
||||
def autotag(parser: settngs.Manager) -> None:
|
||||
# Auto-tag stickies
|
||||
parser.add_setting(
|
||||
"--save-on-low-confidence",
|
||||
@ -238,7 +239,7 @@ def autotag(parser: Manager) -> None:
|
||||
)
|
||||
|
||||
|
||||
def validate_settings(options: dict[str, dict[str, Any]], parser: Manager) -> dict[str, dict[str, Any]]:
|
||||
def validate_settings(options: dict[str, dict[str, Any]], parser: settngs.Manager) -> dict[str, dict[str, Any]]:
|
||||
options["identifier"]["publisher_filter"] = [
|
||||
x.strip() for x in options["identifier"]["publisher_filter"] if x.strip()
|
||||
]
|
||||
@ -249,7 +250,7 @@ def validate_settings(options: dict[str, dict[str, Any]], parser: Manager) -> di
|
||||
return options
|
||||
|
||||
|
||||
def register_settings(parser: Manager) -> None:
|
||||
def register_settings(parser: settngs.Manager) -> None:
|
||||
parser.add_group("general", general, False)
|
||||
parser.add_group("internal", internal, False)
|
||||
parser.add_group("identifier", identifier, False)
|
@ -20,11 +20,11 @@ import os
|
||||
import platform
|
||||
from typing import Callable, cast
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtWidgets, uic
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import ComicArchive
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.graphics import graphics_path
|
||||
from comictaggerlib.optionalmsgdialog import OptionalMessageDialog
|
||||
from comictaggerlib.settingswindow import linuxRarHelp, macRarHelp, windowsRarHelp
|
||||
@ -59,7 +59,7 @@ class FileSelectionList(QtWidgets.QWidget):
|
||||
def __init__(
|
||||
self,
|
||||
parent: QtWidgets.QWidget,
|
||||
options: settings.OptionValues,
|
||||
options: settngs.ConfigValues,
|
||||
dirty_flag_verification: Callable[[str, str], bool],
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -7,7 +7,8 @@ import sys
|
||||
import traceback
|
||||
import types
|
||||
|
||||
from comictaggerlib import settings
|
||||
import settngs
|
||||
|
||||
from comictaggerlib.graphics import graphics_path
|
||||
from comictalker.talkerbase import ComicTalker
|
||||
|
||||
@ -81,12 +82,10 @@ except ImportError as e:
|
||||
qt_available = False
|
||||
|
||||
|
||||
def open_tagger_window(
|
||||
talker_api: ComicTalker, options: settings.OptionValues, gui_exception: Exception | None
|
||||
) -> None:
|
||||
def open_tagger_window(talker_api: ComicTalker, options: settngs.Config, gui_exception: Exception | None) -> None:
|
||||
os.environ["QtWidgets.QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
|
||||
args = []
|
||||
if options["runtime"]["darkmode"]:
|
||||
if options[0]["runtime"]["darkmode"]:
|
||||
args.extend(["-platform", "windows:darkmode=2"])
|
||||
args.extend(sys.argv)
|
||||
app = Application(args)
|
||||
@ -97,7 +96,7 @@ def open_tagger_window(
|
||||
raise SystemExit(1)
|
||||
|
||||
# needed to catch initial open file events (macOS)
|
||||
app.openFileRequest.connect(lambda x: options["runtime"]["files"].append(x.toLocalFile()))
|
||||
app.openFileRequest.connect(lambda x: options[0]["runtime"]["files"].append(x.toLocalFile()))
|
||||
|
||||
if platform.system() == "Darwin":
|
||||
# Set the MacOS dock icon
|
||||
@ -125,7 +124,7 @@ def open_tagger_window(
|
||||
QtWidgets.QApplication.processEvents()
|
||||
|
||||
try:
|
||||
tagger_window = TaggerWindow(options["runtime"]["files"], options, talker_api)
|
||||
tagger_window = TaggerWindow(options[0]["runtime"]["files"], options, talker_api)
|
||||
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
|
||||
tagger_window.show()
|
||||
|
||||
|
@ -20,13 +20,13 @@ import logging
|
||||
import sys
|
||||
from typing import Any, Callable
|
||||
|
||||
import settngs
|
||||
from typing_extensions import NotRequired, TypedDict
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import ComicArchive
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comicapi.issuestring import IssueString
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.imagefetcher import ImageFetcher, ImageFetcherException
|
||||
from comictaggerlib.imagehasher import ImageHasher
|
||||
from comictaggerlib.resulttypes import IssueResult
|
||||
@ -72,7 +72,7 @@ class IssueIdentifier:
|
||||
result_one_good_match = 4
|
||||
result_multiple_good_matches = 5
|
||||
|
||||
def __init__(self, comic_archive: ComicArchive, options: settings.OptionValues, talker_api: ComicTalker) -> None:
|
||||
def __init__(self, comic_archive: ComicArchive, options: settngs.ConfigValues, talker_api: ComicTalker) -> None:
|
||||
self.options = options
|
||||
self.talker_api = talker_api
|
||||
self.comic_archive: ComicArchive = comic_archive
|
||||
|
@ -17,10 +17,10 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from comicapi.issuestring import IssueString
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.ui import ui_path
|
||||
from comictaggerlib.ui.qtutils import reduce_widget_font_size
|
||||
@ -44,7 +44,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
|
||||
def __init__(
|
||||
self,
|
||||
parent: QtWidgets.QWidget,
|
||||
options: settings.OptionValues,
|
||||
options: settngs.ConfigValues,
|
||||
talker_api: ComicTalker,
|
||||
series_id: int,
|
||||
issue_number: str,
|
||||
|
@ -24,9 +24,11 @@ import signal
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
import settngs
|
||||
|
||||
import comictalker.comictalkerapi as ct_api
|
||||
from comicapi import utils
|
||||
from comictaggerlib import cli, settings
|
||||
from comictaggerlib import cli, ctoptions
|
||||
from comictaggerlib.ctversion import version
|
||||
from comictaggerlib.log import setup_logging
|
||||
from comictalker.talkerbase import TalkerError
|
||||
@ -63,8 +65,8 @@ class App:
|
||||
"""docstring for App"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.options: dict[str, dict[str, Any]] = {}
|
||||
self.initial_arg_parser = settings.initial_cmd_line_parser()
|
||||
self.options = settngs.Config({}, {})
|
||||
self.initial_arg_parser = ctoptions.initial_cmd_line_parser()
|
||||
|
||||
def run(self) -> None:
|
||||
opts = self.initialize()
|
||||
@ -81,29 +83,33 @@ class App:
|
||||
return opts
|
||||
|
||||
def register_options(self) -> None:
|
||||
self.manager = settings.Manager(
|
||||
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",
|
||||
)
|
||||
settings.register_commandline(self.manager)
|
||||
settings.register_settings(self.manager)
|
||||
ctoptions.register_commandline(self.manager)
|
||||
ctoptions.register_settings(self.manager)
|
||||
|
||||
def parse_options(self, config_paths: settings.ComicTaggerPaths) -> None:
|
||||
options = self.manager.parse_options(config_paths.user_config_dir / "settings.json")
|
||||
self.options = settings.validate_commandline_options(options, self.manager)
|
||||
self.options = settings.validate_settings(options, self.manager)
|
||||
def parse_options(self, config_paths: ctoptions.ComicTaggerPaths) -> None:
|
||||
config, success = self.manager.parse_config(config_paths.user_config_dir / "settings.json")
|
||||
options, definitions = config
|
||||
if not success:
|
||||
raise SystemExit(99)
|
||||
options = ctoptions.validate_commandline_options(options, self.manager)
|
||||
options = ctoptions.validate_settings(options, self.manager)
|
||||
self.options = settngs.Config(options, definitions)
|
||||
|
||||
def initialize_dirs(self) -> None:
|
||||
self.options["runtime"]["config"].user_data_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options["runtime"]["config"].user_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options["runtime"]["config"].user_cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options["runtime"]["config"].user_state_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options["runtime"]["config"].user_log_dir.mkdir(parents=True, exist_ok=True)
|
||||
logger.debug("user_data_dir: %s", self.options["runtime"]["config"].user_data_dir)
|
||||
logger.debug("user_config_dir: %s", self.options["runtime"]["config"].user_config_dir)
|
||||
logger.debug("user_cache_dir: %s", self.options["runtime"]["config"].user_cache_dir)
|
||||
logger.debug("user_state_dir: %s", self.options["runtime"]["config"].user_state_dir)
|
||||
logger.debug("user_log_dir: %s", self.options["runtime"]["config"].user_log_dir)
|
||||
self.options[0]["runtime"]["config"].user_data_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options[0]["runtime"]["config"].user_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options[0]["runtime"]["config"].user_cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options[0]["runtime"]["config"].user_state_dir.mkdir(parents=True, exist_ok=True)
|
||||
self.options[0]["runtime"]["config"].user_log_dir.mkdir(parents=True, exist_ok=True)
|
||||
logger.debug("user_data_dir: %s", self.options[0]["runtime"]["config"].user_data_dir)
|
||||
logger.debug("user_config_dir: %s", self.options[0]["runtime"]["config"].user_config_dir)
|
||||
logger.debug("user_cache_dir: %s", self.options[0]["runtime"]["config"].user_cache_dir)
|
||||
logger.debug("user_state_dir: %s", self.options[0]["runtime"]["config"].user_state_dir)
|
||||
logger.debug("user_log_dir: %s", self.options[0]["runtime"]["config"].user_log_dir)
|
||||
|
||||
def ctmain(self) -> None:
|
||||
assert self.options is not None
|
||||
@ -111,10 +117,11 @@ class App:
|
||||
|
||||
# manage the CV API key
|
||||
# None comparison is used so that the empty string can unset the value
|
||||
if self.options["comicvine"]["cv_api_key"] is not None or self.options["comicvine"]["cv_url"] is not None:
|
||||
self.manager.save_file(self.options, self.options["runtime"]["config"].user_config_dir / "settings.json")
|
||||
logger.debug(pprint.pformat(self.options))
|
||||
if self.options["commands"]["only_set_cv_key"]:
|
||||
if self.options[0]["comicvine"]["cv_api_key"] is not None or self.options[0]["comicvine"]["cv_url"] is not None:
|
||||
settings_path = self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
|
||||
self.manager.save_file(self.options[0], settings_path)
|
||||
logger.debug(pprint.pformat(self.options[0]))
|
||||
if self.options[0]["commands"]["only_set_cv_key"]:
|
||||
print("Key set") # noqa: T201
|
||||
return
|
||||
|
||||
@ -132,33 +139,33 @@ class App:
|
||||
logger.debug("%s\t%s", pkg.metadata["Name"], pkg.metadata["Version"])
|
||||
|
||||
utils.load_publishers()
|
||||
update_publishers(self.options)
|
||||
update_publishers(self.options[0])
|
||||
|
||||
if not qt_available and not self.options["runtime"]["no_gui"]:
|
||||
self.options["runtime"]["no_gui"] = True
|
||||
if not qt_available and not self.options[0]["runtime"]["no_gui"]:
|
||||
self.options[0]["runtime"]["no_gui"] = True
|
||||
logger.warning("PyQt5 is not available. ComicTagger is limited to command-line mode.")
|
||||
|
||||
gui_exception = None
|
||||
try:
|
||||
talker_api = ct_api.get_comic_talker("comicvine")( # type: ignore[call-arg]
|
||||
version=version,
|
||||
cache_folder=self.options["runtime"]["config"].user_cache_dir,
|
||||
series_match_thresh=self.options["comicvine"]["series_match_search_thresh"],
|
||||
remove_html_tables=self.options["comicvine"]["remove_html_tables"],
|
||||
use_series_start_as_volume=self.options["comicvine"]["use_series_start_as_volume"],
|
||||
wait_on_ratelimit=self.options["autotag"]["wait_and_retry_on_rate_limit"],
|
||||
api_url=self.options["comicvine"]["cv_url"],
|
||||
api_key=self.options["comicvine"]["cv_api_key"],
|
||||
cache_folder=self.options[0]["runtime"]["config"].user_cache_dir,
|
||||
series_match_thresh=self.options[0]["comicvine"]["series_match_search_thresh"],
|
||||
remove_html_tables=self.options[0]["comicvine"]["remove_html_tables"],
|
||||
use_series_start_as_volume=self.options[0]["comicvine"]["use_series_start_as_volume"],
|
||||
wait_on_ratelimit=self.options[0]["autotag"]["wait_and_retry_on_rate_limit"],
|
||||
api_url=self.options[0]["comicvine"]["cv_url"],
|
||||
api_key=self.options[0]["comicvine"]["cv_api_key"],
|
||||
)
|
||||
except TalkerError as e:
|
||||
logger.exception("Unable to load talker")
|
||||
gui_exception = e
|
||||
if self.options["runtime"]["no_gui"]:
|
||||
if self.options[0]["runtime"]["no_gui"]:
|
||||
raise SystemExit(1)
|
||||
|
||||
if self.options["runtime"]["no_gui"]:
|
||||
if self.options[0]["runtime"]["no_gui"]:
|
||||
try:
|
||||
cli.CLI(self.options, talker_api).run()
|
||||
cli.CLI(self.options[0], talker_api).run()
|
||||
except Exception:
|
||||
logger.exception("CLI mode failed")
|
||||
else:
|
||||
|
@ -18,10 +18,10 @@ from __future__ import annotations
|
||||
import logging
|
||||
import os
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtWidgets, uic
|
||||
|
||||
from comicapi.comicarchive import ComicArchive
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.resulttypes import IssueResult
|
||||
from comictaggerlib.ui import ui_path
|
||||
@ -39,7 +39,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
|
||||
parent: QtWidgets.QWidget,
|
||||
matches: list[IssueResult],
|
||||
comic_archive: ComicArchive,
|
||||
options: settings.OptionValues,
|
||||
options: settngs.Values,
|
||||
talker_api: ComicTalker,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
|
@ -17,12 +17,12 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtWidgets, uic
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import ComicArchive, MetaDataStyle
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
|
||||
from comictaggerlib.settingswindow import SettingsWindow
|
||||
from comictaggerlib.ui import ui_path
|
||||
@ -38,7 +38,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
parent: QtWidgets.QWidget,
|
||||
comic_archive_list: list[ComicArchive],
|
||||
data_style: int,
|
||||
options: settings.OptionValues,
|
||||
options: settngs.Config,
|
||||
talker_api: ComicTalker,
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
@ -61,19 +61,19 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
self.rename_list: list[str] = []
|
||||
|
||||
self.btnSettings.clicked.connect(self.modify_settings)
|
||||
platform = "universal" if self.options["filename"]["rename_strict"] else "auto"
|
||||
self.renamer = FileRenamer(None, platform=platform, replacements=self.options["rename"]["replacements"])
|
||||
platform = "universal" if self.options[0]["filename"]["rename_strict"] else "auto"
|
||||
self.renamer = FileRenamer(None, platform=platform, replacements=self.options[0]["rename"]["replacements"])
|
||||
|
||||
self.do_preview()
|
||||
|
||||
def config_renamer(self, ca: ComicArchive, md: GenericMetadata | None = None) -> str:
|
||||
self.renamer.set_template(self.options["filename"]["rename_template"])
|
||||
self.renamer.set_issue_zero_padding(self.options["filename"]["rename_issue_number_padding"])
|
||||
self.renamer.set_smart_cleanup(self.options["filename"]["rename_use_smart_string_cleanup"])
|
||||
self.renamer.replacements = self.options["rename"]["replacements"]
|
||||
self.renamer.set_template(self.options[0]["filename"]["rename_template"])
|
||||
self.renamer.set_issue_zero_padding(self.options[0]["filename"]["rename_issue_number_padding"])
|
||||
self.renamer.set_smart_cleanup(self.options[0]["filename"]["rename_use_smart_string_cleanup"])
|
||||
self.renamer.replacements = self.options[0]["rename"]["replacements"]
|
||||
|
||||
new_ext = ca.path.suffix # default
|
||||
if self.options["filename"]["rename_set_extension_based_on_archive"]:
|
||||
if self.options[0]["filename"]["rename_set_extension_based_on_archive"]:
|
||||
if ca.is_sevenzip():
|
||||
new_ext = ".cb7"
|
||||
elif ca.is_zip():
|
||||
@ -85,13 +85,13 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
md = ca.read_metadata(self.data_style)
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(
|
||||
self.options["filename"]["complicated_parser"],
|
||||
self.options["filename"]["remove_c2c"],
|
||||
self.options["filename"]["remove_fcbd"],
|
||||
self.options["filename"]["remove_publisher"],
|
||||
self.options[0]["filename"]["complicated_parser"],
|
||||
self.options[0]["filename"]["remove_c2c"],
|
||||
self.options[0]["filename"]["remove_fcbd"],
|
||||
self.options[0]["filename"]["remove_publisher"],
|
||||
)
|
||||
self.renamer.set_metadata(md)
|
||||
self.renamer.move = self.options["filename"]["rename_move_to_dir"]
|
||||
self.renamer.move = self.options[0]["filename"]["rename_move_to_dir"]
|
||||
return new_ext
|
||||
|
||||
def do_preview(self) -> None:
|
||||
@ -104,7 +104,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
try:
|
||||
new_name = self.renamer.determine_name(new_ext)
|
||||
except ValueError as e:
|
||||
logger.exception("Invalid format string: %s", self.options["filename"]["rename_template"])
|
||||
logger.exception("Invalid format string: %s", self.options[0]["filename"]["rename_template"])
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
@ -119,7 +119,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
"Formatter failure: %s metadata: %s",
|
||||
self.options["filename"]["rename_template"],
|
||||
self.options[0]["filename"]["rename_template"],
|
||||
self.renamer.metadata,
|
||||
)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
@ -197,7 +197,9 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
|
||||
folder = get_rename_dir(
|
||||
comic[0],
|
||||
self.options["filename"]["rename_dir"] if self.options["filename"]["rename_move_to_dir"] else None,
|
||||
self.options[0]["filename"]["rename_dir"]
|
||||
if self.options[0]["filename"]["rename_move_to_dir"]
|
||||
else None,
|
||||
)
|
||||
|
||||
full_path = folder / comic[1]
|
||||
|
@ -1,20 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from comictaggerlib.settings.cmdline import initial_cmd_line_parser, register_commandline, validate_commandline_options
|
||||
from comictaggerlib.settings.file import register_settings, validate_settings
|
||||
from comictaggerlib.settings.manager import Manager, OptionDefinitions, OptionValues, defaults, save_file
|
||||
from comictaggerlib.settings.types import ComicTaggerPaths
|
||||
|
||||
__all__ = [
|
||||
"initial_cmd_line_parser",
|
||||
"register_commandline",
|
||||
"register_settings",
|
||||
"validate_commandline_options",
|
||||
"validate_settings",
|
||||
"Manager",
|
||||
"ComicTaggerPaths",
|
||||
"OptionValues",
|
||||
"OptionDefinitions",
|
||||
"save_file",
|
||||
"defaults",
|
||||
]
|
@ -1,401 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import pathlib
|
||||
from collections import defaultdict
|
||||
from collections.abc import Sequence
|
||||
from typing import TYPE_CHECKING, Any, Callable, NoReturn, Union
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Setting:
|
||||
def __init__(
|
||||
self,
|
||||
# From argparse
|
||||
*names: str,
|
||||
action: type[argparse.Action] | None = None,
|
||||
nargs: str | int | None = None,
|
||||
const: str | None = None,
|
||||
default: str | None = None,
|
||||
type: Callable[..., Any] | None = None, # noqa: A002
|
||||
choices: Sequence[Any] | None = None,
|
||||
required: bool | None = None,
|
||||
help: str | None = None, # noqa: A002
|
||||
metavar: str | None = None,
|
||||
dest: str | None = None,
|
||||
# ComicTagger
|
||||
cmdline: bool = True,
|
||||
file: bool = True,
|
||||
group: str = "",
|
||||
exclusive: bool = False,
|
||||
):
|
||||
if not names:
|
||||
raise ValueError("names must be specified")
|
||||
# We prefix the destination name used by argparse so that there are no conflicts
|
||||
# Argument names will still cause an exception if there is a conflict e.g. if '-f' is defined twice
|
||||
self.internal_name, dest, flag = self.get_dest(group, names, dest)
|
||||
args: Sequence[str] = names
|
||||
|
||||
# We then also set the metavar so that '--config' in the group runtime shows as 'CONFIG' instead of 'RUNTIME_CONFIG'
|
||||
if not metavar and action not in ("store_true", "store_false", "count"):
|
||||
metavar = dest.upper()
|
||||
|
||||
# If we are not a flag, no '--' or '-' in front
|
||||
# we prefix the first name with the group as argparse sets dest to args[0]
|
||||
# I believe internal name may be able to be used here
|
||||
if not flag:
|
||||
args = tuple((f"{group}_{names[0]}".lstrip("_"), *names[1:]))
|
||||
|
||||
self.action = action
|
||||
self.nargs = nargs
|
||||
self.const = const
|
||||
self.default = default
|
||||
self.type = type
|
||||
self.choices = choices
|
||||
self.required = required
|
||||
self.help = help
|
||||
self.metavar = metavar
|
||||
self.dest = dest
|
||||
self.cmdline = cmdline
|
||||
self.file = file
|
||||
self.argparse_args = args
|
||||
self.group = group
|
||||
self.exclusive = exclusive
|
||||
|
||||
self.argparse_kwargs = {
|
||||
"action": action,
|
||||
"nargs": nargs,
|
||||
"const": const,
|
||||
"default": default,
|
||||
"type": type,
|
||||
"choices": choices,
|
||||
"required": required,
|
||||
"help": help,
|
||||
"metavar": metavar,
|
||||
"dest": self.internal_name if flag else None,
|
||||
}
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Setting({self.argparse_args}, type={self.type}, file={self.file}, cmdline={self.cmdline}, kwargs={self.argparse_kwargs})"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self.__str__()
|
||||
|
||||
def get_dest(self, prefix: str, names: Sequence[str], dest: str | None) -> tuple[str, str, bool]:
|
||||
dest_name = None
|
||||
flag = False
|
||||
|
||||
for n in names:
|
||||
if n.startswith("--"):
|
||||
flag = True
|
||||
dest_name = n.lstrip("-").replace("-", "_")
|
||||
break
|
||||
if n.startswith("-"):
|
||||
flag = True
|
||||
|
||||
if dest_name is None:
|
||||
dest_name = names[0]
|
||||
if dest:
|
||||
dest_name = dest
|
||||
if dest_name is None:
|
||||
raise Exception("Something failed, try again")
|
||||
|
||||
internal_name = f"{prefix}_{dest_name}".lstrip("_")
|
||||
return internal_name, dest_name, flag
|
||||
|
||||
def filter_argparse_kwargs(self) -> dict[str, Any]:
|
||||
return {k: v for k, v in self.argparse_kwargs.items() if v is not None}
|
||||
|
||||
def to_argparse(self) -> tuple[Sequence[str], dict[str, Any]]:
|
||||
return self.argparse_args, self.filter_argparse_kwargs()
|
||||
|
||||
|
||||
OptionValues = dict[str, dict[str, Any]]
|
||||
OptionDefinitions = dict[str, dict[str, Setting]]
|
||||
if TYPE_CHECKING:
|
||||
ArgParser = Union[argparse._MutuallyExclusiveGroup, argparse._ArgumentGroup, argparse.ArgumentParser]
|
||||
|
||||
|
||||
def get_option(options: OptionValues | argparse.Namespace, setting: Setting) -> tuple[Any, bool]:
|
||||
"""
|
||||
Helper function to retrieve the value for a setting and if the value is the default value
|
||||
|
||||
Args:
|
||||
options: Dictionary or namespace of options
|
||||
setting: The setting object describing the value to retrieve
|
||||
"""
|
||||
if isinstance(options, dict):
|
||||
value = options.get(setting.group, {}).get(setting.dest, setting.default)
|
||||
else:
|
||||
value = getattr(options, setting.internal_name, setting.default)
|
||||
return value, value == setting.default
|
||||
|
||||
|
||||
def normalize_options(
|
||||
raw_options: OptionValues | argparse.Namespace,
|
||||
definitions: OptionDefinitions,
|
||||
file: bool = False,
|
||||
cmdline: bool = False,
|
||||
defaults: bool = True,
|
||||
raw_options_2: OptionValues | argparse.Namespace | None = None,
|
||||
) -> OptionValues:
|
||||
"""
|
||||
Creates an `OptionValues` dictionary with setting definitions taken from `self.definitions`
|
||||
and values taken from `raw_options` and `raw_options_2' if defined.
|
||||
Values are assigned so if the value is a dictionary mutating it will mutate the original.
|
||||
|
||||
Args:
|
||||
raw_options: The dict or Namespace to normalize options from
|
||||
definitions: The definition of the options
|
||||
file: Include file options
|
||||
cmdline: Include cmdline options
|
||||
defaults: Include default values in the returned dict
|
||||
raw_options_2: If set, merges non-default values into the returned dict
|
||||
"""
|
||||
options: OptionValues = {}
|
||||
for group_name, group in definitions.items():
|
||||
group_options = {}
|
||||
for setting_name, setting in group.items():
|
||||
if (setting.cmdline and cmdline) or (setting.file and file):
|
||||
# Ensures the option exists with the default if not already set
|
||||
value, default = get_option(raw_options, setting)
|
||||
if not default or default and defaults:
|
||||
group_options[setting_name] = value
|
||||
|
||||
# will override with option from raw_options_2 if it is not the default
|
||||
if raw_options_2 is not None:
|
||||
value, default = get_option(raw_options_2, setting)
|
||||
if not default:
|
||||
group_options[setting_name] = value
|
||||
options[group_name] = group_options
|
||||
options["definitions"] = definitions
|
||||
return options
|
||||
|
||||
|
||||
def parse_file(filename: pathlib.Path, definitions: OptionDefinitions) -> OptionValues:
|
||||
"""
|
||||
Helper function to read options from a json dictionary from a file
|
||||
Args:
|
||||
filename: A pathlib.Path object to read a json dictionary from
|
||||
"""
|
||||
options: OptionValues = {}
|
||||
if filename.exists():
|
||||
try:
|
||||
with filename.open() as file:
|
||||
opts = json.load(file)
|
||||
if isinstance(opts, dict):
|
||||
options = opts
|
||||
except Exception:
|
||||
logger.exception("Failed to load config file: %s", filename)
|
||||
else:
|
||||
logger.info("No config file found")
|
||||
|
||||
return normalize_options(options, definitions, file=True)
|
||||
|
||||
|
||||
def clean_options(
|
||||
options: OptionValues | argparse.Namespace, definitions: OptionDefinitions, file: bool = False, cmdline: bool = True
|
||||
) -> OptionValues:
|
||||
"""
|
||||
Normalizes options and then cleans up empty groups and removes 'definitions'
|
||||
Args:
|
||||
options:
|
||||
file:
|
||||
cmdline:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
clean_options = normalize_options(options, definitions, file=file, cmdline=cmdline)
|
||||
del clean_options["definitions"]
|
||||
for group in list(clean_options.keys()):
|
||||
if not clean_options[group]:
|
||||
del clean_options[group]
|
||||
return clean_options
|
||||
|
||||
|
||||
def defaults(definitions: OptionDefinitions) -> OptionValues:
|
||||
return normalize_options({}, definitions, file=True, cmdline=True)
|
||||
|
||||
|
||||
def get_namespace(options: OptionValues, definitions: OptionDefinitions, defaults: bool = True) -> argparse.Namespace:
|
||||
"""
|
||||
Returns an argparse.Namespace object with options in the form "{group_name}_{setting_name}"
|
||||
`options` should already be normalized.
|
||||
Throws an exception if the internal_name is duplicated
|
||||
|
||||
Args:
|
||||
options: Normalized options to turn into a Namespace
|
||||
defaults: Include default values in the returned dict
|
||||
"""
|
||||
options = normalize_options(options, definitions, file=True, cmdline=True)
|
||||
namespace = argparse.Namespace()
|
||||
for group_name, group in definitions.items():
|
||||
for setting_name, setting in group.items():
|
||||
if hasattr(namespace, setting.internal_name):
|
||||
raise Exception(f"Duplicate internal name: {setting.internal_name}")
|
||||
value, default = get_option(options, setting)
|
||||
|
||||
if not default or default and defaults:
|
||||
setattr(
|
||||
namespace,
|
||||
setting.internal_name,
|
||||
value,
|
||||
)
|
||||
setattr(namespace, "definitions", definitions)
|
||||
return namespace
|
||||
|
||||
|
||||
def save_file(
|
||||
options: OptionValues | argparse.Namespace, definitions: OptionDefinitions, filename: pathlib.Path
|
||||
) -> bool:
|
||||
"""
|
||||
Helper function to save options from a json dictionary to a file
|
||||
Args:
|
||||
options: The options to save to a json dictionary
|
||||
filename: A pathlib.Path object to save the json dictionary to
|
||||
"""
|
||||
file_options = clean_options(options, definitions, file=True)
|
||||
if not filename.exists():
|
||||
filename.parent.mkdir(exist_ok=True, parents=True)
|
||||
filename.touch()
|
||||
|
||||
try:
|
||||
json_str = json.dumps(file_options, indent=2)
|
||||
filename.write_text(json_str, encoding="utf-8")
|
||||
except Exception:
|
||||
logger.exception("Failed to save config file: %s", filename)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Manager:
|
||||
"""docstring for SettingManager"""
|
||||
|
||||
def __init__(
|
||||
self, description: str | None = None, epilog: str | None = None, definitions: OptionDefinitions | None = None
|
||||
):
|
||||
# This one is never used, it just makes MyPy happy
|
||||
self.argparser = argparse.ArgumentParser(description=description, epilog=epilog)
|
||||
self.description = description
|
||||
self.epilog = epilog
|
||||
|
||||
self.definitions: OptionDefinitions = defaultdict(lambda: dict())
|
||||
if definitions:
|
||||
self.definitions = definitions
|
||||
|
||||
self.exclusive_group = False
|
||||
self.current_group_name = ""
|
||||
|
||||
def add_setting(self, *args: Any, **kwargs: Any) -> None:
|
||||
"""Takes passes all arguments through to `Setting`, `group` and `exclusive` are already set"""
|
||||
setting = Setting(*args, group=self.current_group_name, exclusive=self.exclusive_group, **kwargs)
|
||||
self.definitions[self.current_group_name][setting.dest] = setting
|
||||
|
||||
def create_argparser(self) -> None:
|
||||
"""Creates an :class:`argparse.ArgumentParser` from all cmdline settings"""
|
||||
groups: dict[str, ArgParser] = {}
|
||||
self.argparser = argparse.ArgumentParser(
|
||||
description=self.description,
|
||||
epilog=self.epilog,
|
||||
formatter_class=argparse.RawTextHelpFormatter,
|
||||
)
|
||||
for group_name, group in self.definitions.items():
|
||||
for setting_name, setting in group.items():
|
||||
if setting.cmdline:
|
||||
argparse_args, argparse_kwargs = setting.to_argparse()
|
||||
current_group: ArgParser = self.argparser
|
||||
if setting.group:
|
||||
if setting.group not in groups:
|
||||
if setting.exclusive:
|
||||
groups[setting.group] = self.argparser.add_argument_group(
|
||||
setting.group,
|
||||
).add_mutually_exclusive_group()
|
||||
else:
|
||||
groups[setting.group] = self.argparser.add_argument_group(setting.group)
|
||||
|
||||
# hard coded exception for files
|
||||
if not (setting.group == "runtime" and setting.nargs == "*"):
|
||||
current_group = groups[setting.group]
|
||||
current_group.add_argument(*argparse_args, **argparse_kwargs)
|
||||
|
||||
def add_group(self, name: str, add_settings: Callable[[Manager], None], exclusive_group: bool = False) -> None:
|
||||
"""
|
||||
The primary way to add define options on this class
|
||||
|
||||
Args:
|
||||
name: The name of the group to define
|
||||
add_settings: A function that registers individual options using :meth:`add_setting`
|
||||
exclusive_group: If this group is an argparse exclusive group
|
||||
"""
|
||||
self.current_group_name = name
|
||||
self.exclusive_group = exclusive_group
|
||||
add_settings(self)
|
||||
self.current_group_name = ""
|
||||
self.exclusive_group = False
|
||||
|
||||
def exit(self, *args: Any, **kwargs: Any) -> NoReturn:
|
||||
"""See :class:`~argparse.ArgumentParser`"""
|
||||
self.argparser.exit(*args, **kwargs)
|
||||
raise SystemExit(99)
|
||||
|
||||
def defaults(self) -> OptionValues:
|
||||
return defaults(self.definitions)
|
||||
|
||||
def clean_options(
|
||||
self, options: OptionValues | argparse.Namespace, file: bool = False, cmdline: bool = True
|
||||
) -> OptionValues:
|
||||
return clean_options(options=options, definitions=self.definitions, file=file, cmdline=cmdline)
|
||||
|
||||
def normalize_options(
|
||||
self,
|
||||
raw_options: OptionValues | argparse.Namespace,
|
||||
file: bool = False,
|
||||
cmdline: bool = False,
|
||||
defaults: bool = True,
|
||||
raw_options_2: OptionValues | argparse.Namespace | None = None,
|
||||
) -> OptionValues:
|
||||
return normalize_options(
|
||||
raw_options=raw_options,
|
||||
definitions=self.definitions,
|
||||
file=file,
|
||||
cmdline=cmdline,
|
||||
defaults=defaults,
|
||||
raw_options_2=raw_options_2,
|
||||
)
|
||||
|
||||
def get_namespace(self, options: OptionValues, defaults: bool = True) -> argparse.Namespace:
|
||||
return get_namespace(options=options, definitions=self.definitions, defaults=defaults)
|
||||
|
||||
def parse_file(self, filename: pathlib.Path) -> OptionValues:
|
||||
return parse_file(filename=filename, definitions=self.definitions)
|
||||
|
||||
def save_file(self, options: OptionValues | argparse.Namespace, filename: pathlib.Path) -> bool:
|
||||
return save_file(options=options, definitions=self.definitions, filename=filename)
|
||||
|
||||
def parse_args(self, args: list[str] | None = None, namespace: argparse.Namespace | None = None) -> OptionValues:
|
||||
"""
|
||||
Creates an `argparse.ArgumentParser` from cmdline settings in `self.definitions`.
|
||||
`args` and `namespace` are passed to `argparse.ArgumentParser.parse_args`
|
||||
|
||||
Args:
|
||||
args: Passed to argparse.ArgumentParser.parse
|
||||
namespace: Passed to argparse.ArgumentParser.parse
|
||||
"""
|
||||
self.create_argparser()
|
||||
ns = self.argparser.parse_args(args, namespace=namespace)
|
||||
|
||||
return normalize_options(ns, definitions=self.definitions, cmdline=True, file=True)
|
||||
|
||||
def parse_options(self, config_path: pathlib.Path, args: list[str] | None = None) -> OptionValues:
|
||||
file_options = self.parse_file(
|
||||
config_path,
|
||||
)
|
||||
cli_options = self.parse_args(args, self.get_namespace(file_options, defaults=False))
|
||||
|
||||
final_options = normalize_options(cli_options, definitions=self.definitions, file=True, cmdline=True)
|
||||
return final_options
|
@ -22,11 +22,11 @@ import pathlib
|
||||
import platform
|
||||
from typing import Any
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.genericmetadata import md_test
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.ctversion import version
|
||||
from comictaggerlib.filerenamer import FileRenamer, Replacement, Replacements
|
||||
from comictaggerlib.imagefetcher import ImageFetcher
|
||||
@ -130,7 +130,7 @@ Spider-Geddon #1 - New Players; Check In
|
||||
|
||||
|
||||
class SettingsWindow(QtWidgets.QDialog):
|
||||
def __init__(self, parent: QtWidgets.QWidget, options: settings.OptionValues, talker_api: ComicTalker) -> None:
|
||||
def __init__(self, parent: QtWidgets.QWidget, options: settngs.Config, talker_api: ComicTalker) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
uic.loadUi(ui_path / "settingswindow.ui", self)
|
||||
@ -269,52 +269,54 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def settings_to_form(self) -> None:
|
||||
# Copy values from settings to form
|
||||
self.leRarExePath.setText(self.options["general"]["rar_exe_path"])
|
||||
self.sbNameMatchIdentifyThresh.setValue(self.options["identifier"]["series_match_identify_thresh"])
|
||||
self.sbNameMatchSearchThresh.setValue(self.options["comicvine"]["series_match_search_thresh"])
|
||||
self.tePublisherFilter.setPlainText("\n".join(self.options["identifier"]["publisher_filter"]))
|
||||
self.leRarExePath.setText(self.options[0]["general"]["rar_exe_path"])
|
||||
self.sbNameMatchIdentifyThresh.setValue(self.options[0]["identifier"]["series_match_identify_thresh"])
|
||||
self.sbNameMatchSearchThresh.setValue(self.options[0]["comicvine"]["series_match_search_thresh"])
|
||||
self.tePublisherFilter.setPlainText("\n".join(self.options[0]["identifier"]["publisher_filter"]))
|
||||
|
||||
self.cbxCheckForNewVersion.setChecked(self.options["general"]["check_for_new_version"])
|
||||
self.cbxCheckForNewVersion.setChecked(self.options[0]["general"]["check_for_new_version"])
|
||||
|
||||
self.cbxComplicatedParser.setChecked(self.options["filename"]["complicated_parser"])
|
||||
self.cbxRemoveC2C.setChecked(self.options["filename"]["remove_c2c"])
|
||||
self.cbxRemoveFCBD.setChecked(self.options["filename"]["remove_fcbd"])
|
||||
self.cbxRemovePublisher.setChecked(self.options["filename"]["remove_publisher"])
|
||||
self.cbxComplicatedParser.setChecked(self.options[0]["filename"]["complicated_parser"])
|
||||
self.cbxRemoveC2C.setChecked(self.options[0]["filename"]["remove_c2c"])
|
||||
self.cbxRemoveFCBD.setChecked(self.options[0]["filename"]["remove_fcbd"])
|
||||
self.cbxRemovePublisher.setChecked(self.options[0]["filename"]["remove_publisher"])
|
||||
self.switch_parser()
|
||||
|
||||
self.cbxUseSeriesStartAsVolume.setChecked(self.options["comicvine"]["use_series_start_as_volume"])
|
||||
self.cbxClearFormBeforePopulating.setChecked(self.options["comicvine"]["clear_form_before_populating_from_cv"])
|
||||
self.cbxRemoveHtmlTables.setChecked(self.options["comicvine"]["remove_html_tables"])
|
||||
self.cbxUseSeriesStartAsVolume.setChecked(self.options[0]["comicvine"]["use_series_start_as_volume"])
|
||||
self.cbxClearFormBeforePopulating.setChecked(
|
||||
self.options[0]["comicvine"]["clear_form_before_populating_from_cv"]
|
||||
)
|
||||
self.cbxRemoveHtmlTables.setChecked(self.options[0]["comicvine"]["remove_html_tables"])
|
||||
|
||||
self.cbxUseFilter.setChecked(self.options["comicvine"]["always_use_publisher_filter"])
|
||||
self.cbxSortByYear.setChecked(self.options["comicvine"]["sort_series_by_year"])
|
||||
self.cbxExactMatches.setChecked(self.options["comicvine"]["exact_series_matches_first"])
|
||||
self.cbxUseFilter.setChecked(self.options[0]["comicvine"]["always_use_publisher_filter"])
|
||||
self.cbxSortByYear.setChecked(self.options[0]["comicvine"]["sort_series_by_year"])
|
||||
self.cbxExactMatches.setChecked(self.options[0]["comicvine"]["exact_series_matches_first"])
|
||||
|
||||
self.leKey.setText(self.options["comicvine"]["cv_api_key"])
|
||||
self.leURL.setText(self.options["comicvine"]["cv_url"])
|
||||
self.leKey.setText(self.options[0]["comicvine"]["cv_api_key"])
|
||||
self.leURL.setText(self.options[0]["comicvine"]["cv_url"])
|
||||
|
||||
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.options["cbl"]["assume_lone_credit_is_primary"])
|
||||
self.cbxCopyCharactersToTags.setChecked(self.options["cbl"]["copy_characters_to_tags"])
|
||||
self.cbxCopyTeamsToTags.setChecked(self.options["cbl"]["copy_teams_to_tags"])
|
||||
self.cbxCopyLocationsToTags.setChecked(self.options["cbl"]["copy_locations_to_tags"])
|
||||
self.cbxCopyStoryArcsToTags.setChecked(self.options["cbl"]["copy_storyarcs_to_tags"])
|
||||
self.cbxCopyNotesToComments.setChecked(self.options["cbl"]["copy_notes_to_comments"])
|
||||
self.cbxCopyWebLinkToComments.setChecked(self.options["cbl"]["copy_weblink_to_comments"])
|
||||
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.options["cbl"]["apply_cbl_transform_on_cv_import"])
|
||||
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.options[0]["cbl"]["assume_lone_credit_is_primary"])
|
||||
self.cbxCopyCharactersToTags.setChecked(self.options[0]["cbl"]["copy_characters_to_tags"])
|
||||
self.cbxCopyTeamsToTags.setChecked(self.options[0]["cbl"]["copy_teams_to_tags"])
|
||||
self.cbxCopyLocationsToTags.setChecked(self.options[0]["cbl"]["copy_locations_to_tags"])
|
||||
self.cbxCopyStoryArcsToTags.setChecked(self.options[0]["cbl"]["copy_storyarcs_to_tags"])
|
||||
self.cbxCopyNotesToComments.setChecked(self.options[0]["cbl"]["copy_notes_to_comments"])
|
||||
self.cbxCopyWebLinkToComments.setChecked(self.options[0]["cbl"]["copy_weblink_to_comments"])
|
||||
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"])
|
||||
self.cbxApplyCBLTransformOnBatchOperation.setChecked(
|
||||
self.options["cbl"]["apply_cbl_transform_on_bulk_operation"]
|
||||
self.options[0]["cbl"]["apply_cbl_transform_on_bulk_operation"]
|
||||
)
|
||||
|
||||
self.leRenameTemplate.setText(self.options["rename"]["template"])
|
||||
self.leIssueNumPadding.setText(str(self.options["rename"]["issue_number_padding"]))
|
||||
self.cbxSmartCleanup.setChecked(self.options["rename"]["use_smart_string_cleanup"])
|
||||
self.cbxChangeExtension.setChecked(self.options["rename"]["set_extension_based_on_archive"])
|
||||
self.cbxMoveFiles.setChecked(self.options["rename"]["move_to_dir"])
|
||||
self.leDirectory.setText(self.options["rename"]["dir"])
|
||||
self.cbxRenameStrict.setChecked(self.options["rename"]["strict"])
|
||||
self.leRenameTemplate.setText(self.options[0]["rename"]["template"])
|
||||
self.leIssueNumPadding.setText(str(self.options[0]["rename"]["issue_number_padding"]))
|
||||
self.cbxSmartCleanup.setChecked(self.options[0]["rename"]["use_smart_string_cleanup"])
|
||||
self.cbxChangeExtension.setChecked(self.options[0]["rename"]["set_extension_based_on_archive"])
|
||||
self.cbxMoveFiles.setChecked(self.options[0]["rename"]["move_to_dir"])
|
||||
self.leDirectory.setText(self.options[0]["rename"]["dir"])
|
||||
self.cbxRenameStrict.setChecked(self.options[0]["rename"]["strict"])
|
||||
|
||||
for table, replacments in zip(
|
||||
(self.twLiteralReplacements, self.twValueReplacements), self.options["rename"]["replacements"]
|
||||
(self.twLiteralReplacements, self.twValueReplacements), self.options[0]["rename"]["replacements"]
|
||||
):
|
||||
table.clearContents()
|
||||
for i in reversed(range(table.rowCount())):
|
||||
@ -349,7 +351,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["rename"]["template"])
|
||||
logger.exception("Invalid format string: %s", self.options[0]["rename"]["template"])
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
@ -363,7 +365,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
return
|
||||
else:
|
||||
logger.exception(
|
||||
"Formatter failure: %s metadata: %s", self.options["rename"]["template"], self.renamer.metadata
|
||||
"Formatter failure: %s metadata: %s", self.options[0]["rename"]["template"], self.renamer.metadata
|
||||
)
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
@ -376,72 +378,70 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
)
|
||||
|
||||
# Copy values from form to settings and save
|
||||
self.options["general"]["rar_exe_path"] = str(self.leRarExePath.text())
|
||||
self.options[0]["general"]["rar_exe_path"] = str(self.leRarExePath.text())
|
||||
|
||||
# make sure rar program is now in the path for the rar class
|
||||
if self.options["general"]["rar_exe_path"]:
|
||||
utils.add_to_path(os.path.dirname(self.options["general"]["rar_exe_path"]))
|
||||
if self.options[0]["general"]["rar_exe_path"]:
|
||||
utils.add_to_path(os.path.dirname(self.options[0]["general"]["rar_exe_path"]))
|
||||
|
||||
if not str(self.leIssueNumPadding.text()).isdigit():
|
||||
self.leIssueNumPadding.setText("0")
|
||||
|
||||
self.options["general"]["check_for_new_version"] = self.cbxCheckForNewVersion.isChecked()
|
||||
self.options[0]["general"]["check_for_new_version"] = self.cbxCheckForNewVersion.isChecked()
|
||||
|
||||
self.options["identifier"]["series_match_identify_thresh"] = self.sbNameMatchIdentifyThresh.value()
|
||||
self.options["comicvine"]["series_match_search_thresh"] = self.sbNameMatchSearchThresh.value()
|
||||
self.options["identifier"]["publisher_filter"] = [
|
||||
self.options[0]["identifier"]["series_match_identify_thresh"] = self.sbNameMatchIdentifyThresh.value()
|
||||
self.options[0]["comicvine"]["series_match_search_thresh"] = self.sbNameMatchSearchThresh.value()
|
||||
self.options[0]["identifier"]["publisher_filter"] = [
|
||||
x.strip() for x in str(self.tePublisherFilter.toPlainText()).splitlines() if x.strip()
|
||||
]
|
||||
|
||||
self.options["filename"]["complicated_parser"] = self.cbxComplicatedParser.isChecked()
|
||||
self.options["filename"]["remove_c2c"] = self.cbxRemoveC2C.isChecked()
|
||||
self.options["filename"]["remove_fcbd"] = self.cbxRemoveFCBD.isChecked()
|
||||
self.options["filename"]["remove_publisher"] = self.cbxRemovePublisher.isChecked()
|
||||
self.options[0]["filename"]["complicated_parser"] = self.cbxComplicatedParser.isChecked()
|
||||
self.options[0]["filename"]["remove_c2c"] = self.cbxRemoveC2C.isChecked()
|
||||
self.options[0]["filename"]["remove_fcbd"] = self.cbxRemoveFCBD.isChecked()
|
||||
self.options[0]["filename"]["remove_publisher"] = self.cbxRemovePublisher.isChecked()
|
||||
|
||||
self.options["comicvine"]["use_series_start_as_volume"] = self.cbxUseSeriesStartAsVolume.isChecked()
|
||||
self.options["comicvine"][
|
||||
self.options[0]["comicvine"]["use_series_start_as_volume"] = self.cbxUseSeriesStartAsVolume.isChecked()
|
||||
self.options[0]["comicvine"][
|
||||
"clear_form_before_populating_from_cv"
|
||||
] = self.cbxClearFormBeforePopulating.isChecked()
|
||||
self.options["comicvine"]["remove_html_tables"] = self.cbxRemoveHtmlTables.isChecked()
|
||||
self.options[0]["comicvine"]["remove_html_tables"] = self.cbxRemoveHtmlTables.isChecked()
|
||||
|
||||
self.options["comicvine"]["always_use_publisher_filter"] = self.cbxUseFilter.isChecked()
|
||||
self.options["comicvine"]["sort_series_by_year"] = self.cbxSortByYear.isChecked()
|
||||
self.options["comicvine"]["exact_series_matches_first"] = self.cbxExactMatches.isChecked()
|
||||
self.options[0]["comicvine"]["always_use_publisher_filter"] = self.cbxUseFilter.isChecked()
|
||||
self.options[0]["comicvine"]["sort_series_by_year"] = self.cbxSortByYear.isChecked()
|
||||
self.options[0]["comicvine"]["exact_series_matches_first"] = self.cbxExactMatches.isChecked()
|
||||
|
||||
if self.leKey.text().strip():
|
||||
self.options["comicvine"]["cv_api_key"] = self.leKey.text().strip()
|
||||
self.talker_api.api_key = self.options["comicvine"]["cv_api_key"]
|
||||
self.options[0]["comicvine"]["cv_api_key"] = self.leKey.text().strip()
|
||||
self.talker_api.api_key = self.options[0]["comicvine"]["cv_api_key"]
|
||||
|
||||
if self.leURL.text().strip():
|
||||
self.options["comicvine"]["cv_url"] = self.leURL.text().strip()
|
||||
self.talker_api.api_url = self.options["comicvine"]["cv_url"]
|
||||
self.options[0]["comicvine"]["cv_url"] = self.leURL.text().strip()
|
||||
self.talker_api.api_url = self.options[0]["comicvine"]["cv_url"]
|
||||
|
||||
self.options["cbl"]["assume_lone_credit_is_primary"] = self.cbxAssumeLoneCreditIsPrimary.isChecked()
|
||||
self.options["cbl"]["copy_characters_to_tags"] = self.cbxCopyCharactersToTags.isChecked()
|
||||
self.options["cbl"]["copy_teams_to_tags"] = self.cbxCopyTeamsToTags.isChecked()
|
||||
self.options["cbl"]["copy_locations_to_tags"] = self.cbxCopyLocationsToTags.isChecked()
|
||||
self.options["cbl"]["copy_storyarcs_to_tags"] = self.cbxCopyStoryArcsToTags.isChecked()
|
||||
self.options["cbl"]["copy_notes_to_comments"] = self.cbxCopyNotesToComments.isChecked()
|
||||
self.options["cbl"]["copy_weblink_to_comments"] = self.cbxCopyWebLinkToComments.isChecked()
|
||||
self.options["cbl"]["apply_cbl_transform_on_cv_import"] = self.cbxApplyCBLTransformOnCVIMport.isChecked()
|
||||
self.options["cbl"][
|
||||
self.options[0]["cbl"]["assume_lone_credit_is_primary"] = self.cbxAssumeLoneCreditIsPrimary.isChecked()
|
||||
self.options[0]["cbl"]["copy_characters_to_tags"] = self.cbxCopyCharactersToTags.isChecked()
|
||||
self.options[0]["cbl"]["copy_teams_to_tags"] = self.cbxCopyTeamsToTags.isChecked()
|
||||
self.options[0]["cbl"]["copy_locations_to_tags"] = self.cbxCopyLocationsToTags.isChecked()
|
||||
self.options[0]["cbl"]["copy_storyarcs_to_tags"] = self.cbxCopyStoryArcsToTags.isChecked()
|
||||
self.options[0]["cbl"]["copy_notes_to_comments"] = self.cbxCopyNotesToComments.isChecked()
|
||||
self.options[0]["cbl"]["copy_weblink_to_comments"] = self.cbxCopyWebLinkToComments.isChecked()
|
||||
self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"] = self.cbxApplyCBLTransformOnCVIMport.isChecked()
|
||||
self.options[0]["cbl"][
|
||||
"apply_cbl_transform_on_bulk_operation"
|
||||
] = self.cbxApplyCBLTransformOnBatchOperation.isChecked()
|
||||
|
||||
self.options["rename"]["template"] = str(self.leRenameTemplate.text())
|
||||
self.options["rename"]["issue_number_padding"] = int(self.leIssueNumPadding.text())
|
||||
self.options["rename"]["use_smart_string_cleanup"] = self.cbxSmartCleanup.isChecked()
|
||||
self.options["rename"]["set_extension_based_on_archive"] = self.cbxChangeExtension.isChecked()
|
||||
self.options["rename"]["move_to_dir"] = self.cbxMoveFiles.isChecked()
|
||||
self.options["rename"]["dir"] = self.leDirectory.text()
|
||||
self.options[0]["rename"]["template"] = str(self.leRenameTemplate.text())
|
||||
self.options[0]["rename"]["issue_number_padding"] = int(self.leIssueNumPadding.text())
|
||||
self.options[0]["rename"]["use_smart_string_cleanup"] = self.cbxSmartCleanup.isChecked()
|
||||
self.options[0]["rename"]["set_extension_based_on_archive"] = self.cbxChangeExtension.isChecked()
|
||||
self.options[0]["rename"]["move_to_dir"] = self.cbxMoveFiles.isChecked()
|
||||
self.options[0]["rename"]["dir"] = self.leDirectory.text()
|
||||
|
||||
self.options["rename"]["strict"] = self.cbxRenameStrict.isChecked()
|
||||
self.options["rename"]["replacements"] = self.get_replacemnts()
|
||||
self.options[0]["rename"]["strict"] = self.cbxRenameStrict.isChecked()
|
||||
self.options[0]["rename"]["replacements"] = self.get_replacemnts()
|
||||
|
||||
settings.save_file(
|
||||
self.options,
|
||||
self.options["definitions"],
|
||||
self.options["runtime"]["config"].user_config_dir / "settings.json",
|
||||
settngs.save_file(
|
||||
self.options[0], self.options[1], self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
|
||||
)
|
||||
self.parent().options = self.options
|
||||
QtWidgets.QDialog.accept(self)
|
||||
@ -450,8 +450,8 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.select_file(self.leRarExePath, "RAR")
|
||||
|
||||
def clear_cache(self) -> None:
|
||||
ImageFetcher(self.options["runtime"]["config"].cache_folder).clear_cache()
|
||||
ComicCacher(self.options["runtime"]["config"].cache_folder, version).clear_cache()
|
||||
ImageFetcher(self.options[0]["runtime"]["config"].cache_folder).clear_cache()
|
||||
ComicCacher(self.options[0]["runtime"]["config"].cache_folder, version).clear_cache()
|
||||
QtWidgets.QMessageBox.information(self, self.name, "Cache has been cleared.")
|
||||
|
||||
def test_api_key(self) -> None:
|
||||
@ -461,7 +461,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
QtWidgets.QMessageBox.warning(self, "API Key Test", "Key is NOT valid.")
|
||||
|
||||
def reset_settings(self) -> None:
|
||||
self.options = settings.defaults(self.options["definitions"])
|
||||
self.options = settngs.Config(settngs.defaults(self.options[1]), self.options[1])
|
||||
self.settings_to_form()
|
||||
QtWidgets.QMessageBox.information(self, self.name, self.name + " have been returned to default values.")
|
||||
|
||||
|
@ -31,6 +31,7 @@ from typing import Any, Callable
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import natsort
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets, uic
|
||||
|
||||
from comicapi import utils
|
||||
@ -39,7 +40,7 @@ from comicapi.comicinfoxml import ComicInfoXml
|
||||
from comicapi.filenameparser import FileNameParser
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comicapi.issuestring import IssueString
|
||||
from comictaggerlib import ctversion, settings
|
||||
from comictaggerlib import ctversion
|
||||
from comictaggerlib.applicationlogwindow import ApplicationLogWindow, QTextEditLogger
|
||||
from comictaggerlib.autotagmatchwindow import AutoTagMatchWindow
|
||||
from comictaggerlib.autotagprogresswindow import AutoTagProgressWindow
|
||||
@ -78,7 +79,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
def __init__(
|
||||
self,
|
||||
file_list: list[str],
|
||||
options: settings.OptionValues,
|
||||
options: settngs.Config,
|
||||
talker_api: ComicTalker,
|
||||
parent: QtWidgets.QWidget | None = None,
|
||||
) -> None:
|
||||
@ -87,17 +88,17 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
uic.loadUi(ui_path / "taggerwindow.ui", self)
|
||||
self.options = options
|
||||
if not options:
|
||||
self.options = {}
|
||||
self.options = ({}, {})
|
||||
self.talker_api = talker_api
|
||||
self.log_window = self.setup_logger()
|
||||
|
||||
# prevent multiple instances
|
||||
socket = QtNetwork.QLocalSocket(self)
|
||||
socket.connectToServer(options["internal"]["install_id"])
|
||||
socket.connectToServer(options[0]["internal"]["install_id"])
|
||||
alive = socket.waitForConnected(3000)
|
||||
if alive:
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.info("Another application with key [%s] is already running", options["internal"]["install_id"])
|
||||
logger.info("Another application with key [%s] is already running", options[0]["internal"]["install_id"])
|
||||
# send file list to other instance
|
||||
if file_list:
|
||||
socket.write(pickle.dumps(file_list))
|
||||
@ -109,15 +110,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["internal"]["install_id"])
|
||||
ok = self.socketServer.listen(options[0]["internal"]["install_id"])
|
||||
if not ok:
|
||||
if self.socketServer.serverError() == QtNetwork.QAbstractSocket.SocketError.AddressInUseError:
|
||||
self.socketServer.removeServer(options["internal"]["install_id"])
|
||||
ok = self.socketServer.listen(options["internal"]["install_id"])
|
||||
self.socketServer.removeServer(options[0]["internal"]["install_id"])
|
||||
ok = self.socketServer.listen(options[0]["internal"]["install_id"])
|
||||
if not ok:
|
||||
logger.error(
|
||||
"Cannot start local socket with key [%s]. Reason: %s",
|
||||
options["internal"]["install_id"],
|
||||
options[0]["internal"]["install_id"],
|
||||
self.socketServer.errorString(),
|
||||
)
|
||||
sys.exit()
|
||||
@ -131,15 +132,15 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
grid_layout = QtWidgets.QGridLayout(self.tabPages)
|
||||
grid_layout.addWidget(self.page_list_editor)
|
||||
|
||||
self.fileSelectionList = FileSelectionList(self.widgetListHolder, self.options, self.dirty_flag_verification)
|
||||
self.fileSelectionList = FileSelectionList(self.widgetListHolder, self.options[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["internal"]["last_filelist_sorted_column"],
|
||||
QtCore.Qt.SortOrder(self.options["internal"]["last_filelist_sorted_order"]),
|
||||
self.options[0]["internal"]["last_filelist_sorted_column"],
|
||||
QtCore.Qt.SortOrder(self.options[0]["internal"]["last_filelist_sorted_order"]),
|
||||
)
|
||||
|
||||
# we can't specify relative font sizes in the UI designer, so
|
||||
@ -157,13 +158,13 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
|
||||
|
||||
if options["runtime"]["type"] and isinstance(options["runtime"]["type"][0], int):
|
||||
if options[0]["runtime"]["type"] and isinstance(options[0]["runtime"]["type"][0], int):
|
||||
# respect the command line option tag type
|
||||
options["internal"]["last_selected_save_data_style"] = options["runtime"]["type"][0]
|
||||
options["internal"]["last_selected_load_data_style"] = options["runtime"]["type"][0]
|
||||
options[0]["internal"]["last_selected_save_data_style"] = options[0]["runtime"]["type"][0]
|
||||
options[0]["internal"]["last_selected_load_data_style"] = options[0]["runtime"]["type"][0]
|
||||
|
||||
self.save_data_style = options["internal"]["last_selected_save_data_style"]
|
||||
self.load_data_style = options["internal"]["last_selected_load_data_style"]
|
||||
self.save_data_style = options[0]["internal"]["last_selected_save_data_style"]
|
||||
self.load_data_style = options[0]["internal"]["last_selected_load_data_style"]
|
||||
|
||||
self.setAcceptDrops(True)
|
||||
self.config_menus()
|
||||
@ -228,9 +229,12 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
self.show()
|
||||
self.set_app_position()
|
||||
if self.options["internal"]["last_form_side_width"] != -1:
|
||||
if self.options[0]["internal"]["last_form_side_width"] != -1:
|
||||
self.splitter.setSizes(
|
||||
[self.options["internal"]["last_form_side_width"], self.options["internal"]["last_list_side_width"]]
|
||||
[
|
||||
self.options[0]["internal"]["last_form_side_width"],
|
||||
self.options[0]["internal"]["last_list_side_width"],
|
||||
]
|
||||
)
|
||||
self.raise_()
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -248,7 +252,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
if len(file_list) != 0:
|
||||
self.fileSelectionList.add_path_list(file_list)
|
||||
|
||||
if self.options["dialog"]["show_disclaimer"]:
|
||||
if self.options[0]["dialog"]["show_disclaimer"]:
|
||||
checked = OptionalMessageDialog.msg(
|
||||
self,
|
||||
"Welcome!",
|
||||
@ -263,9 +267,9 @@ use ComicTagger on local copies of your comics.<br><br>
|
||||
Have fun!
|
||||
""",
|
||||
)
|
||||
self.options["dialog"]["show_disclaimer"] = not checked
|
||||
self.options[0]["dialog"]["show_disclaimer"] = not checked
|
||||
|
||||
if self.options["general"]["check_for_new_version"]:
|
||||
if self.options[0]["general"]["check_for_new_version"]:
|
||||
self.check_latest_version_online()
|
||||
|
||||
def open_file_event(self, url: QtCore.QUrl) -> None:
|
||||
@ -278,7 +282,7 @@ Have fun!
|
||||
|
||||
def setup_logger(self) -> ApplicationLogWindow:
|
||||
try:
|
||||
current_logs = (self.options["runtime"]["config"].user_log_dir / "ComicTagger.log").read_text("utf-8")
|
||||
current_logs = (self.options[0]["runtime"]["config"].user_log_dir / "ComicTagger.log").read_text("utf-8")
|
||||
except Exception:
|
||||
current_logs = ""
|
||||
root_logger = logging.getLogger()
|
||||
@ -611,10 +615,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["filename"]["complicated_parser"],
|
||||
self.options["filename"]["remove_c2c"],
|
||||
self.options["filename"]["remove_fcbd"],
|
||||
self.options["filename"]["remove_publisher"],
|
||||
self.options[0]["filename"]["complicated_parser"],
|
||||
self.options[0]["filename"]["remove_c2c"],
|
||||
self.options[0]["filename"]["remove_fcbd"],
|
||||
self.options[0]["filename"]["remove_publisher"],
|
||||
)
|
||||
if len(self.metadata.pages) == 0 and self.comic_archive is not None:
|
||||
self.metadata.set_default_page_list(self.comic_archive.get_number_of_pages())
|
||||
@ -982,10 +986,10 @@ Have fun!
|
||||
# copy the form onto metadata object
|
||||
self.form_to_metadata()
|
||||
new_metadata = self.comic_archive.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[0]["filename"]["complicated_parser"],
|
||||
self.options[0]["filename"]["remove_c2c"],
|
||||
self.options[0]["filename"]["remove_fcbd"],
|
||||
self.options[0]["filename"]["remove_publisher"],
|
||||
split_words,
|
||||
)
|
||||
if new_metadata is not None:
|
||||
@ -1006,8 +1010,8 @@ Have fun!
|
||||
else:
|
||||
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFiles)
|
||||
|
||||
if self.options["internal"]["last_opened_folder"] is not None:
|
||||
dialog.setDirectory(self.options["internal"]["last_opened_folder"])
|
||||
if self.options[0]["internal"]["last_opened_folder"] is not None:
|
||||
dialog.setDirectory(self.options[0]["internal"]["last_opened_folder"])
|
||||
|
||||
if not folder_mode:
|
||||
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar *.cb7 *.7z)"
|
||||
@ -1057,7 +1061,7 @@ Have fun!
|
||||
issue_count,
|
||||
cover_index_list,
|
||||
self.comic_archive,
|
||||
self.options,
|
||||
self.options[0],
|
||||
self.talker_api,
|
||||
autoselect,
|
||||
literal,
|
||||
@ -1089,10 +1093,10 @@ Have fun!
|
||||
else:
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
if new_metadata is not None:
|
||||
if self.options["cbl"]["apply_cbl_transform_on_cv_import"]:
|
||||
new_metadata = CBLTransformer(new_metadata, self.options).apply()
|
||||
if self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"]:
|
||||
new_metadata = CBLTransformer(new_metadata, self.options[0]).apply()
|
||||
|
||||
if self.options["comicvine"]["clear_form_before_populating_from_cv"]:
|
||||
if self.options[0]["comicvine"]["clear_form_before_populating_from_cv"]:
|
||||
self.clear_form()
|
||||
|
||||
notes = (
|
||||
@ -1147,7 +1151,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["internal"]["last_selected_load_data_style"] = self.load_data_style
|
||||
self.options[0]["internal"]["last_selected_load_data_style"] = self.load_data_style
|
||||
self.update_menus()
|
||||
if self.comic_archive is not None:
|
||||
self.load_archive(self.comic_archive)
|
||||
@ -1158,7 +1162,7 @@ Have fun!
|
||||
|
||||
def set_save_data_style(self, s: int) -> None:
|
||||
self.save_data_style = self.cbSaveDataStyle.itemData(s)
|
||||
self.options["internal"]["last_selected_save_data_style"] = self.save_data_style
|
||||
self.options[0]["internal"]["last_selected_save_data_style"] = self.save_data_style
|
||||
self.update_style_tweaks()
|
||||
self.update_menus()
|
||||
|
||||
@ -1383,10 +1387,13 @@ Have fun!
|
||||
settingswin.result()
|
||||
|
||||
def set_app_position(self) -> None:
|
||||
if self.options["internal"]["last_main_window_width"] != 0:
|
||||
self.move(self.options["internal"]["last_main_window_x"], self.options["internal"]["last_main_window_y"])
|
||||
if self.options[0]["internal"]["last_main_window_width"] != 0:
|
||||
self.move(
|
||||
self.options[0]["internal"]["last_main_window_x"], self.options[0]["internal"]["last_main_window_y"]
|
||||
)
|
||||
self.resize(
|
||||
self.options["internal"]["last_main_window_width"], self.options["internal"]["last_main_window_height"]
|
||||
self.options[0]["internal"]["last_main_window_width"],
|
||||
self.options[0]["internal"]["last_main_window_height"],
|
||||
)
|
||||
else:
|
||||
screen = QtGui.QGuiApplication.primaryScreen().geometry()
|
||||
@ -1656,9 +1663,9 @@ Have fun!
|
||||
|
||||
if (
|
||||
dest_style == MetaDataStyle.CBI
|
||||
and self.options["cbl"]["apply_cbl_transform_on_bulk_operation"]
|
||||
and self.options[0]["cbl"]["apply_cbl_transform_on_bulk_operation"]
|
||||
):
|
||||
md = CBLTransformer(md, self.options).apply()
|
||||
md = CBLTransformer(md, self.options[0]).apply()
|
||||
|
||||
if not ca.write_metadata(md, dest_style):
|
||||
failed_list.append(ca.path)
|
||||
@ -1696,8 +1703,8 @@ Have fun!
|
||||
logger.exception("Save aborted.")
|
||||
|
||||
if not ct_md.is_empty:
|
||||
if self.options["cbl"]["apply_cbl_transform_on_cv_import"]:
|
||||
ct_md = CBLTransformer(ct_md, self.options).apply()
|
||||
if self.options[0]["cbl"]["apply_cbl_transform_on_cv_import"]:
|
||||
ct_md = CBLTransformer(ct_md, self.options[0]).apply()
|
||||
|
||||
QtWidgets.QApplication.restoreOverrideCursor()
|
||||
|
||||
@ -1716,7 +1723,7 @@ Have fun!
|
||||
self, ca: ComicArchive, match_results: OnlineMatchResults, dlg: AutoTagStartWindow
|
||||
) -> tuple[bool, OnlineMatchResults]:
|
||||
success = False
|
||||
ii = IssueIdentifier(ca, self.options, self.talker_api)
|
||||
ii = IssueIdentifier(ca, self.options[0], self.talker_api)
|
||||
|
||||
# read in metadata, and parse file name if not there
|
||||
try:
|
||||
@ -1726,10 +1733,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["filename"]["complicated_parser"],
|
||||
self.options["filename"]["remove_c2c"],
|
||||
self.options["filename"]["remove_fcbd"],
|
||||
self.options["filename"]["remove_publisher"],
|
||||
self.options[0]["filename"]["complicated_parser"],
|
||||
self.options[0]["filename"]["remove_c2c"],
|
||||
self.options[0]["filename"]["remove_fcbd"],
|
||||
self.options[0]["filename"]["remove_publisher"],
|
||||
dlg.split_words,
|
||||
)
|
||||
if dlg.ignore_leading_digits_in_filename and md.series is not None:
|
||||
@ -1815,7 +1822,7 @@ Have fun!
|
||||
)
|
||||
md.overlay(ct_md.replace(notes=utils.combine_notes(md.notes, notes, "Tagged with ComicTagger")))
|
||||
|
||||
if self.options["comicvine"]["auto_imprint"]:
|
||||
if self.options[0]["comicvine"]["auto_imprint"]:
|
||||
md.fix_publisher()
|
||||
|
||||
if not ca.write_metadata(md, self.save_data_style):
|
||||
@ -1844,7 +1851,7 @@ Have fun!
|
||||
|
||||
atstartdlg = AutoTagStartWindow(
|
||||
self,
|
||||
self.options,
|
||||
self.options[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."
|
||||
@ -1947,7 +1954,7 @@ Have fun!
|
||||
match_results.multiple_matches,
|
||||
style,
|
||||
self.actual_issue_data_fetch,
|
||||
self.options,
|
||||
self.options[0],
|
||||
self.talker_api,
|
||||
)
|
||||
matchdlg.setModal(True)
|
||||
@ -1993,20 +2000,18 @@ 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["internal"]["last_main_window_width"] = appsize.width()
|
||||
self.options["internal"]["last_main_window_height"] = appsize.height()
|
||||
self.options["internal"]["last_main_window_x"] = self.x()
|
||||
self.options["internal"]["last_main_window_y"] = self.y()
|
||||
self.options["internal"]["last_form_side_width"] = self.splitter.sizes()[0]
|
||||
self.options["internal"]["last_list_side_width"] = self.splitter.sizes()[1]
|
||||
self.options[0]["internal"]["last_main_window_width"] = appsize.width()
|
||||
self.options[0]["internal"]["last_main_window_height"] = appsize.height()
|
||||
self.options[0]["internal"]["last_main_window_x"] = self.x()
|
||||
self.options[0]["internal"]["last_main_window_y"] = self.y()
|
||||
self.options[0]["internal"]["last_form_side_width"] = self.splitter.sizes()[0]
|
||||
self.options[0]["internal"]["last_list_side_width"] = self.splitter.sizes()[1]
|
||||
(
|
||||
self.options["internal"]["last_filelist_sorted_column"],
|
||||
self.options["internal"]["last_filelist_sorted_order"],
|
||||
self.options[0]["internal"]["last_filelist_sorted_column"],
|
||||
self.options[0]["internal"]["last_filelist_sorted_order"],
|
||||
) = self.fileSelectionList.get_sorting()
|
||||
settings.save_file(
|
||||
self.options,
|
||||
self.options["definitions"],
|
||||
self.options["runtime"]["config"].user_config_dir / "settings.json",
|
||||
settngs.save_file(
|
||||
self.options[0], self.options[1], self.options[0]["runtime"]["config"].user_config_dir / "settings.json"
|
||||
)
|
||||
|
||||
event.accept()
|
||||
@ -2056,7 +2061,7 @@ Have fun!
|
||||
|
||||
def apply_cbl_transform(self) -> None:
|
||||
self.form_to_metadata()
|
||||
self.metadata = CBLTransformer(self.metadata, self.options).apply()
|
||||
self.metadata = CBLTransformer(self.metadata, self.options[0]).apply()
|
||||
self.metadata_to_form()
|
||||
|
||||
def recalc_page_dimensions(self) -> None:
|
||||
@ -2101,7 +2106,7 @@ Have fun!
|
||||
QtCore.QTimer.singleShot(1, self.fileSelectionList.revert_selection)
|
||||
return
|
||||
|
||||
self.options["internal"]["last_opened_folder"] = os.path.abspath(os.path.split(comic_archive.path)[0])
|
||||
self.options[0]["internal"]["last_opened_folder"] = os.path.abspath(os.path.split(comic_archive.path)[0])
|
||||
self.comic_archive = comic_archive
|
||||
try:
|
||||
self.metadata = self.comic_archive.read_metadata(self.load_data_style)
|
||||
@ -2134,12 +2139,12 @@ Have fun!
|
||||
version_checker = VersionChecker()
|
||||
self.version_check_complete(
|
||||
version_checker.get_latest_version(
|
||||
self.options["internal"]["install_id"], self.options["general"]["send_usage_stats"]
|
||||
self.options[0]["internal"]["install_id"], self.options[0]["general"]["send_usage_stats"]
|
||||
)
|
||||
)
|
||||
|
||||
def version_check_complete(self, new_version: tuple[str, str]) -> None:
|
||||
if new_version[0] not in (self.version, self.options["dialog"]["dont_notify_about_this_version"]):
|
||||
if new_version[0] not in (self.version, self.options[0]["dialog"]["dont_notify_about_this_version"]):
|
||||
website = "https://github.com/comictagger/comictagger"
|
||||
checked = OptionalMessageDialog.msg(
|
||||
self,
|
||||
@ -2150,7 +2155,7 @@ Have fun!
|
||||
"Don't tell me about this version again",
|
||||
)
|
||||
if checked:
|
||||
self.options["dialog"]["dont_notify_about_this_version"] = new_version[0]
|
||||
self.options[0]["dialog"]["dont_notify_about_this_version"] = new_version[0]
|
||||
|
||||
def on_incoming_socket_connection(self) -> None:
|
||||
# Accept connection from other instance.
|
||||
|
@ -19,13 +19,13 @@ import itertools
|
||||
import logging
|
||||
from collections import deque
|
||||
|
||||
import settngs
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import ComicArchive
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib import settings
|
||||
from comictaggerlib.coverimagewidget import CoverImageWidget
|
||||
from comictaggerlib.issueidentifier import IssueIdentifier
|
||||
from comictaggerlib.issueselectionwindow import IssueSelectionWindow
|
||||
@ -111,7 +111,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
|
||||
issue_count: int,
|
||||
cover_index_list: list[int],
|
||||
comic_archive: ComicArchive | None,
|
||||
options: settings.OptionValues,
|
||||
options: settngs.ConfigValues,
|
||||
talker_api: ComicTalker,
|
||||
autoselect: bool = False,
|
||||
literal: bool = False,
|
||||
|
@ -8,6 +8,7 @@ pycountry
|
||||
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
|
||||
rapidfuzz>=2.12.0
|
||||
requests==2.*
|
||||
settngs==0.2.0
|
||||
text2digits
|
||||
typing_extensions
|
||||
wordninja
|
||||
|
@ -1,214 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
settings_cases = [
|
||||
(
|
||||
(
|
||||
("--test",),
|
||||
dict(
|
||||
action=None,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=None,
|
||||
help=None,
|
||||
metavar=None,
|
||||
dest=None,
|
||||
cmdline=True,
|
||||
file=True,
|
||||
group="tst",
|
||||
exclusive=False,
|
||||
),
|
||||
), # Equivalent to Setting("--test", group="tst")
|
||||
{
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"cmdline": True,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "test", # dest is calculated by Setting and is not used by argparse
|
||||
"exclusive": False,
|
||||
"file": True,
|
||||
"group": "tst",
|
||||
"help": None,
|
||||
"internal_name": "tst_test", # Should almost always be "{group}_{dest}"
|
||||
"metavar": "TEST", # Set manually so argparse doesn't use TST_TEST
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
"argparse_args": ("--test",), # *args actually sent to argparse
|
||||
"argparse_kwargs": {
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "tst_test",
|
||||
"help": None,
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
}, # Non-None **kwargs sent to argparse
|
||||
},
|
||||
),
|
||||
(
|
||||
(
|
||||
(
|
||||
"-t",
|
||||
"--test",
|
||||
),
|
||||
dict(
|
||||
action=None,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=None,
|
||||
help=None,
|
||||
metavar=None,
|
||||
dest=None,
|
||||
cmdline=True,
|
||||
file=True,
|
||||
group="tst",
|
||||
exclusive=False,
|
||||
),
|
||||
), # Equivalent to Setting("-t", "--test", group="tst")
|
||||
{
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"cmdline": True,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "test",
|
||||
"exclusive": False,
|
||||
"file": True,
|
||||
"group": "tst",
|
||||
"help": None,
|
||||
"internal_name": "tst_test",
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
"argparse_args": (
|
||||
"-t",
|
||||
"--test",
|
||||
), # Only difference with above is here
|
||||
"argparse_kwargs": {
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "tst_test",
|
||||
"help": None,
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
(
|
||||
("test",),
|
||||
dict(
|
||||
action=None,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=None,
|
||||
help=None,
|
||||
metavar=None,
|
||||
dest=None,
|
||||
cmdline=True,
|
||||
file=True,
|
||||
group="tst",
|
||||
exclusive=False,
|
||||
),
|
||||
), # Equivalent to Setting("test", group="tst")
|
||||
{
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"cmdline": True,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "test",
|
||||
"exclusive": False,
|
||||
"file": True,
|
||||
"group": "tst",
|
||||
"help": None,
|
||||
"internal_name": "tst_test",
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
"argparse_args": ("tst_test",),
|
||||
"argparse_kwargs": {
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": None, # Only difference with #1 is here, argparse sets dest based on the *args passed to it
|
||||
"help": None,
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
},
|
||||
},
|
||||
),
|
||||
(
|
||||
(
|
||||
("--test",),
|
||||
dict(
|
||||
action=None,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=None,
|
||||
help=None,
|
||||
metavar=None,
|
||||
dest=None,
|
||||
cmdline=True,
|
||||
file=True,
|
||||
group="",
|
||||
exclusive=False,
|
||||
),
|
||||
), # Equivalent to Setting("test")
|
||||
{
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"cmdline": True,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "test",
|
||||
"exclusive": False,
|
||||
"file": True,
|
||||
"group": "",
|
||||
"help": None,
|
||||
"internal_name": "test", # No group, leading _ is stripped
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
"argparse_args": ("--test",),
|
||||
"argparse_kwargs": {
|
||||
"action": None,
|
||||
"choices": None,
|
||||
"const": None,
|
||||
"default": None,
|
||||
"dest": "test",
|
||||
"help": None,
|
||||
"metavar": "TEST",
|
||||
"nargs": None,
|
||||
"required": None,
|
||||
"type": None,
|
||||
},
|
||||
},
|
||||
),
|
||||
]
|
@ -6,9 +6,9 @@ import comictalker.comiccacher
|
||||
from testing.comicdata import search_results
|
||||
|
||||
|
||||
def test_create_cache(settings, mock_version):
|
||||
comictalker.comiccacher.ComicCacher(settings["runtime"]["config"].user_cache_dir, mock_version[0])
|
||||
assert (settings["runtime"]["config"].user_cache_dir).exists()
|
||||
def test_create_cache(options, mock_version):
|
||||
comictalker.comiccacher.ComicCacher(options["runtime"]["config"].user_cache_dir, mock_version[0])
|
||||
assert (options["runtime"]["config"].user_cache_dir).exists()
|
||||
|
||||
|
||||
def test_search_results(comic_cache):
|
||||
|
@ -9,15 +9,15 @@ from typing import Any
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
import settngs
|
||||
from PIL import Image
|
||||
|
||||
import comicapi.comicarchive
|
||||
import comicapi.genericmetadata
|
||||
import comictaggerlib.settings
|
||||
import comictaggerlib.ctoptions
|
||||
import comictalker.comiccacher
|
||||
import comictalker.talkers.comicvine
|
||||
from comicapi import utils
|
||||
from comictaggerlib import settings as ctsettings
|
||||
from testing import comicvine, filenames
|
||||
from testing.comicdata import all_seed_imprints, seed_imprints
|
||||
|
||||
@ -56,7 +56,7 @@ def no_requests(monkeypatch) -> None:
|
||||
|
||||
@pytest.fixture
|
||||
def comicvine_api(
|
||||
monkeypatch, cbz, comic_cache, mock_version, settings
|
||||
monkeypatch, cbz, comic_cache, mock_version, options
|
||||
) -> 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.
|
||||
@ -117,7 +117,7 @@ def comicvine_api(
|
||||
|
||||
cv = comictalker.talkers.comicvine.ComicVineTalker(
|
||||
version=mock_version[0],
|
||||
cache_folder=settings["runtime"]["config"].user_cache_dir,
|
||||
cache_folder=options["runtime"]["config"].user_cache_dir,
|
||||
api_url="",
|
||||
api_key="",
|
||||
series_match_thresh=90,
|
||||
@ -163,12 +163,12 @@ def seed_all_publishers(monkeypatch):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def settings(settings_manager, tmp_path):
|
||||
def options(settings_manager, tmp_path):
|
||||
|
||||
ctsettings.register_commandline(settings_manager)
|
||||
ctsettings.register_settings(settings_manager)
|
||||
comictaggerlib.ctoptions.register_commandline(settings_manager)
|
||||
comictaggerlib.ctoptions.register_settings(settings_manager)
|
||||
defaults = settings_manager.defaults()
|
||||
defaults["runtime"]["config"] = ctsettings.ComicTaggerPaths(tmp_path / "config")
|
||||
defaults["runtime"]["config"] = comictaggerlib.ctoptions.ComicTaggerPaths(tmp_path / "config")
|
||||
defaults["runtime"]["config"].user_data_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults["runtime"]["config"].user_config_dir.mkdir(parents=True, exist_ok=True)
|
||||
defaults["runtime"]["config"].user_cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
@ -179,10 +179,10 @@ def settings(settings_manager, tmp_path):
|
||||
|
||||
@pytest.fixture
|
||||
def settings_manager():
|
||||
manager = ctsettings.Manager()
|
||||
manager = settngs.Manager()
|
||||
yield manager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def comic_cache(settings, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
|
||||
yield comictalker.comiccacher.ComicCacher(settings["runtime"]["config"].user_cache_dir, mock_version[0])
|
||||
def comic_cache(options, mock_version) -> Generator[comictalker.comiccacher.ComicCacher, Any, None]:
|
||||
yield comictalker.comiccacher.ComicCacher(options["runtime"]["config"].user_cache_dir, mock_version[0])
|
||||
|
@ -9,8 +9,8 @@ import testing.comicdata
|
||||
import testing.comicvine
|
||||
|
||||
|
||||
def test_crop(cbz_double_cover, settings, tmp_path, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, settings, comicvine_api)
|
||||
def test_crop(cbz_double_cover, options, tmp_path, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz_double_cover, options, comicvine_api)
|
||||
cropped = ii.crop_cover(cbz_double_cover.archiver.read_file("double_cover.jpg"))
|
||||
original_cover = cbz_double_cover.get_page(0)
|
||||
|
||||
@ -21,15 +21,15 @@ def test_crop(cbz_double_cover, settings, tmp_path, comicvine_api):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("additional_md, expected", testing.comicdata.metadata_keys)
|
||||
def test_get_search_keys(cbz, settings, additional_md, expected, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
|
||||
def test_get_search_keys(cbz, options, additional_md, expected, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
|
||||
ii.set_additional_metadata(additional_md)
|
||||
|
||||
assert expected == ii.get_search_keys()
|
||||
|
||||
|
||||
def test_get_issue_cover_match_score(cbz, settings, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
|
||||
def test_get_issue_cover_match_score(cbz, options, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
|
||||
score = ii.get_issue_cover_match_score(
|
||||
int(
|
||||
comicapi.issuestring.IssueString(
|
||||
@ -49,8 +49,8 @@ def test_get_issue_cover_match_score(cbz, settings, comicvine_api):
|
||||
assert expected == score
|
||||
|
||||
|
||||
def test_search(cbz, settings, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, settings, comicvine_api)
|
||||
def test_search(cbz, options, comicvine_api):
|
||||
ii = comictaggerlib.issueidentifier.IssueIdentifier(cbz, options, comicvine_api)
|
||||
results = ii.search()
|
||||
cv_expected = {
|
||||
"series": f"{testing.comicvine.cv_volume_result['results']['name']} ({testing.comicvine.cv_volume_result['results']['start_year']})",
|
||||
|
@ -1,152 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
import comictaggerlib.settings.manager
|
||||
from testing.settings import settings_cases
|
||||
|
||||
|
||||
def test_settings_manager():
|
||||
manager = comictaggerlib.settings.manager.Manager()
|
||||
defaults = manager.defaults()
|
||||
assert manager is not None and defaults is not None
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arguments, expected", settings_cases)
|
||||
def test_setting(arguments, expected):
|
||||
assert vars(comictaggerlib.settings.manager.Setting(*arguments[0], **arguments[1])) == expected
|
||||
|
||||
|
||||
def test_add_setting(settings_manager):
|
||||
assert settings_manager.add_setting("--test") is None
|
||||
|
||||
|
||||
def test_get_defaults(settings_manager):
|
||||
settings_manager.add_setting("--test", default="hello")
|
||||
defaults = settings_manager.defaults()
|
||||
assert defaults[""]["test"] == "hello"
|
||||
|
||||
|
||||
def test_get_namespace(settings_manager):
|
||||
settings_manager.add_setting("--test", default="hello")
|
||||
defaults = settings_manager.get_namespace(settings_manager.defaults())
|
||||
assert defaults.test == "hello"
|
||||
|
||||
|
||||
def test_get_defaults_group(settings_manager):
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello"))
|
||||
defaults = settings_manager.defaults()
|
||||
assert defaults["tst"]["test"] == "hello"
|
||||
|
||||
|
||||
def test_get_namespace_group(settings_manager):
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello"))
|
||||
defaults = settings_manager.get_namespace(settings_manager.defaults())
|
||||
assert defaults.tst_test == "hello"
|
||||
|
||||
|
||||
def test_cmdline_only(settings_manager):
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello", file=False))
|
||||
settings_manager.add_group("tst2", lambda parser: parser.add_setting("--test2", default="hello", cmdline=False))
|
||||
|
||||
file_normalized = settings_manager.normalize_options({}, file=True)
|
||||
cmdline_normalized = settings_manager.normalize_options({}, cmdline=True)
|
||||
|
||||
assert "test" in cmdline_normalized["tst"]
|
||||
assert "test2" not in cmdline_normalized["tst2"]
|
||||
|
||||
assert "test" not in file_normalized["tst"]
|
||||
assert "test2" in file_normalized["tst2"]
|
||||
|
||||
|
||||
def test_normalize(settings_manager):
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello"))
|
||||
|
||||
defaults = settings_manager.defaults()
|
||||
defaults["test"] = "fail" # Not defined in settings_manager
|
||||
|
||||
defaults_namespace = settings_manager.get_namespace(defaults)
|
||||
defaults_namespace.test = "fail"
|
||||
|
||||
normalized = settings_manager.normalize_options(defaults, file=True)
|
||||
normalized_namespace = settings_manager.get_namespace(settings_manager.normalize_options(defaults, file=True))
|
||||
|
||||
assert "test" not in normalized
|
||||
assert "tst" in normalized
|
||||
assert "test" in normalized["tst"]
|
||||
assert normalized["tst"]["test"] == "hello"
|
||||
|
||||
assert not hasattr(normalized_namespace, "test")
|
||||
assert hasattr(normalized_namespace, "tst_test")
|
||||
assert normalized_namespace.tst_test == "hello"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"raw, raw2, expected",
|
||||
[
|
||||
({"tst": {"test": "fail"}}, argparse.Namespace(tst_test="success"), "success"),
|
||||
# hello is default so is not used in raw_options_2
|
||||
({"tst": {"test": "success"}}, argparse.Namespace(tst_test="hello"), "success"),
|
||||
(argparse.Namespace(tst_test="fail"), {"tst": {"test": "success"}}, "success"),
|
||||
(argparse.Namespace(tst_test="success"), {"tst": {"test": "hello"}}, "success"),
|
||||
],
|
||||
)
|
||||
def test_normalize_merge(raw, raw2, expected, settings_manager):
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello"))
|
||||
|
||||
normalized = settings_manager.normalize_options(raw, file=True, raw_options_2=raw2)
|
||||
|
||||
assert normalized["tst"]["test"] == expected
|
||||
|
||||
|
||||
def test_cli_set(settings_manager, tmp_path):
|
||||
settings_file = tmp_path / "settings.json"
|
||||
settings_file.write_text(json.dumps({}))
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello", file=False))
|
||||
|
||||
normalized = settings_manager.parse_options(settings_file, ["--test", "success"])
|
||||
|
||||
assert "test" in normalized["tst"]
|
||||
assert normalized["tst"]["test"] == "success"
|
||||
|
||||
|
||||
def test_file_set(settings_manager, tmp_path):
|
||||
settings_file = tmp_path / "settings.json"
|
||||
settings_file.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"tst": {"test": "success"},
|
||||
}
|
||||
)
|
||||
)
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello", cmdline=False))
|
||||
|
||||
normalized = settings_manager.parse_options(settings_file, [])
|
||||
|
||||
assert "test" in normalized["tst"]
|
||||
assert normalized["tst"]["test"] == "success"
|
||||
|
||||
|
||||
def test_cli_override_file(settings_manager, tmp_path):
|
||||
settings_file = tmp_path / "settings.json"
|
||||
settings_file.write_text(json.dumps({"tst": {"test": "fail"}}))
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="hello"))
|
||||
|
||||
normalized = settings_manager.parse_options(settings_file, ["--test", "success"])
|
||||
|
||||
assert "test" in normalized["tst"]
|
||||
assert normalized["tst"]["test"] == "success"
|
||||
|
||||
|
||||
def test_cli_explicit_default(settings_manager, tmp_path):
|
||||
settings_file = tmp_path / "settings.json"
|
||||
settings_file.write_text(json.dumps({"tst": {"test": "fail"}}))
|
||||
settings_manager.add_group("tst", lambda parser: parser.add_setting("--test", default="success"))
|
||||
|
||||
normalized = settings_manager.parse_options(settings_file, ["--test", "success"])
|
||||
|
||||
assert "test" in normalized["tst"]
|
||||
assert normalized["tst"]["test"] == "success"
|
Loading…
Reference in New Issue
Block a user