Compare commits

...

6 Commits

Author SHA1 Message Date
14ce8a759f Mark all QTextEdit's as plain text only 2024-02-26 15:57:00 -08:00
d277eb332b Add an option to disable prompt on save Fixes #422 2024-02-24 19:56:32 -08:00
dcad32ade0 Fix settngs generation 2024-02-24 19:55:28 -08:00
dd0b637566 Bump settngs 2024-02-24 19:01:10 -08:00
920a0ed1af Implement better migration of changed settings should fix #609 2024-02-23 15:45:18 -08:00
9eb50da744 Fix setting rar info in the settings window Fixes #596
Look in all drive letters for rar executable
2024-02-23 15:45:18 -08:00
12 changed files with 290 additions and 80 deletions

View File

@ -41,6 +41,6 @@ repos:
rev: v1.8.0
hooks:
- id: mypy
additional_dependencies: [types-setuptools, types-requests, settngs>=0.9.1]
additional_dependencies: [types-setuptools, types-requests, settngs>=0.10.0]
ci:
skip: [mypy]

View File

@ -11,7 +11,11 @@ def generate() -> str:
app = comictaggerlib.main.App()
app.load_plugins(app.initial_arg_parser.parse_known_args()[0])
app.register_settings()
return settngs.generate_ns(app.manager.definitions)
imports, types = settngs.generate_dict(app.manager.definitions)
imports2, types2 = settngs.generate_ns(app.manager.definitions)
i = imports.splitlines()
i.extend(set(imports2.splitlines()) - set(i))
return "\n\n".join(("\n".join(i), types2, types))
if __name__ == "__main__":

View File

@ -14,7 +14,7 @@ from comictaggerlib.ctsettings.commandline import (
)
from comictaggerlib.ctsettings.file import register_file_settings, validate_file_settings
from comictaggerlib.ctsettings.plugin import group_for_plugin, register_plugin_settings, validate_plugin_settings
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
from comictaggerlib.ctsettings.settngs_namespace import SettngsNS as ct_ns
from comictaggerlib.ctsettings.types import ComicTaggerPaths
from comictalker import ComicTalker

View File

@ -29,7 +29,7 @@ from comicapi import utils
from comicapi.comicarchive import metadata_styles
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib import ctversion
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
from comictaggerlib.ctsettings.settngs_namespace import SettngsNS as ct_ns
from comictaggerlib.ctsettings.types import (
ComicTaggerPaths,
metadata_type,
@ -308,9 +308,12 @@ def validate_commandline_settings(config: settngs.Config[ct_ns], parser: settngs
# take a crack at finding rar exe if it's not in the path
if not utils.which("rar"):
if platform.system() == "Windows":
# look in some likely places for Windows machines
utils.add_to_path(r"C:\Program Files\WinRAR")
utils.add_to_path(r"C:\Program Files (x86)\WinRAR")
letters = ["C"]
letters.extend({f"{d}" for d in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" if os.path.exists(f"{d}:\\")} - {"C"})
for letter in letters:
# look in some likely places for Windows machines
utils.add_to_path(rf"{letters}:\Program Files\WinRAR")
utils.add_to_path(rf"{letters}:\Program Files (x86)\WinRAR")
else:
if platform.system() == "Darwin":
result = subprocess.run(("/usr/libexec/path_helper", "-s"), capture_output=True)

View File

@ -5,7 +5,7 @@ import uuid
import settngs
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
from comictaggerlib.ctsettings.settngs_namespace import SettngsNS as ct_ns
from comictaggerlib.defaults import DEFAULT_REPLACEMENTS, Replacement, Replacements
@ -19,6 +19,12 @@ def general(parser: settngs.Manager) -> None:
help="Disable the ComicRack metadata type",
)
parser.add_setting("use_short_metadata_names", default=False, action=argparse.BooleanOptionalAction, cmdline=False)
parser.add_setting(
"--prompt-on-save",
default=True,
action=argparse.BooleanOptionalAction,
help="Prompts the user to confirm saving tags when using the GUI.",
)
def internal(parser: settngs.Manager) -> None:
@ -254,12 +260,24 @@ def parse_filter(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
return config
def migrate_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
original_types = ("cbi", "cr", "comet")
save_style = config[0].internal__save_data_style
if not isinstance(save_style, list):
if isinstance(save_style, int) and save_style in (0, 1, 2):
config[0].internal__save_data_style = [original_types[save_style]]
elif isinstance(save_style, str):
config[0].internal__save_data_style = [save_style]
else:
config[0].internal__save_data_style = ["cbi"]
return config
def validate_file_settings(config: settngs.Config[ct_ns]) -> settngs.Config[ct_ns]:
config = parse_filter(config)
# TODO Remove this conversion check at a later date
if isinstance(config[0].internal__save_data_style, str):
config[0].internal__save_data_style = [config[0].internal__save_data_style]
config = migrate_settings(config)
if config[0].Filename_Parsing__protofolius_issue_number_scheme:
config[0].Filename_Parsing__allow_issue_start_with_letter = True

View File

@ -10,16 +10,16 @@ import comicapi.comicarchive
import comicapi.utils
import comictaggerlib.ctsettings
from comicapi.comicarchive import Archiver
from comictaggerlib.ctsettings.settngs_namespace import settngs_namespace as ct_ns
from comictaggerlib.ctsettings.settngs_namespace import SettngsNS as ct_ns
from comictalker.comictalker import ComicTalker
logger = logging.getLogger("comictagger")
def group_for_plugin(plugin: Archiver | ComicTalker) -> str:
def group_for_plugin(plugin: Archiver | ComicTalker | type[Archiver]) -> str:
if isinstance(plugin, ComicTalker):
return f"Source {plugin.id}"
if isinstance(plugin, Archiver):
if isinstance(plugin, Archiver) or plugin == Archiver:
return "Archive"
raise NotImplementedError(f"Invalid plugin received: {plugin=}")

View File

@ -1,5 +1,7 @@
from __future__ import annotations
import typing
import settngs
import comicapi.genericmetadata
@ -8,7 +10,7 @@ import comictaggerlib.defaults
import comictaggerlib.resulttypes
class settngs_namespace(settngs.TypedNS):
class SettngsNS(settngs.TypedNS):
Commands__version: bool
Commands__command: comictaggerlib.resulttypes.Action
Commands__copy: str
@ -98,6 +100,7 @@ class settngs_namespace(settngs.TypedNS):
General__check_for_new_version: bool
General__disable_cr: bool
General__use_short_metadata_names: bool
General__prompt_on_save: bool
Dialog_Flags__show_disclaimer: bool
Dialog_Flags__dont_notify_about_this_version: str
@ -108,3 +111,150 @@ class settngs_namespace(settngs.TypedNS):
Source_comicvine__comicvine_key: str
Source_comicvine__comicvine_url: str
Source_comicvine__cv_use_series_start_as_volume: bool
class Commands(typing.TypedDict):
version: bool
command: comictaggerlib.resulttypes.Action
copy: str
class Runtime_Options(typing.TypedDict):
config: comictaggerlib.ctsettings.types.ComicTaggerPaths
verbose: int
abort_on_conflict: bool
delete_original: bool
parse_filename: bool
issue_id: str
online: bool
metadata: comicapi.genericmetadata.GenericMetadata
interactive: bool
abort_on_low_confidence: bool
summary: bool
raw: bool
recursive: bool
dryrun: bool
darkmode: bool
glob: bool
quiet: bool
json: bool
type: list[str]
overwrite: bool
no_gui: bool
files: list[str]
class internal(typing.TypedDict):
install_id: str
save_data_style: list[str]
load_data_style: str
last_opened_folder: str
window_width: int
window_height: int
window_x: int
window_y: int
form_width: int
list_width: int
sort_column: int
sort_direction: int
class Issue_Identifier(typing.TypedDict):
series_match_identify_thresh: int
border_crop_percent: int
publisher_filter: list[str]
series_match_search_thresh: int
clear_metadata: bool
auto_imprint: bool
sort_series_by_year: bool
exact_series_matches_first: bool
always_use_publisher_filter: bool
class Filename_Parsing(typing.TypedDict):
complicated_parser: bool
remove_c2c: bool
remove_fcbd: bool
remove_publisher: bool
split_words: bool
protofolius_issue_number_scheme: bool
allow_issue_start_with_letter: bool
class Sources(typing.TypedDict):
source: str
remove_html_tables: bool
class Comic_Book_Lover(typing.TypedDict):
assume_lone_credit_is_primary: bool
copy_characters_to_tags: bool
copy_teams_to_tags: bool
copy_locations_to_tags: bool
copy_storyarcs_to_tags: bool
copy_notes_to_comments: bool
copy_weblink_to_comments: bool
apply_transform_on_import: bool
apply_transform_on_bulk_operation: bool
class File_Rename(typing.TypedDict):
template: str
issue_number_padding: int
use_smart_string_cleanup: bool
auto_extension: bool
dir: str
move_to_dir: bool
strict: bool
replacements: comictaggerlib.defaults.Replacements
class Auto_Tag(typing.TypedDict):
save_on_low_confidence: bool
dont_use_year_when_identifying: bool
assume_issue_one: bool
ignore_leading_numbers_in_filename: bool
remove_archive_after_successful_match: bool
class General(typing.TypedDict):
check_for_new_version: bool
disable_cr: bool
use_short_metadata_names: bool
prompt_on_save: bool
class Dialog_Flags(typing.TypedDict):
show_disclaimer: bool
dont_notify_about_this_version: str
ask_about_usage_stats: bool
class Archive(typing.TypedDict):
rar: str
class Source_comicvine(typing.TypedDict):
comicvine_key: str
comicvine_url: str
cv_use_series_start_as_volume: bool
SettngsDict = typing.TypedDict(
"SettngsDict",
{
"Commands": Commands,
"Runtime Options": Runtime_Options,
"internal": internal,
"Issue Identifier": Issue_Identifier,
"Filename Parsing": Filename_Parsing,
"Sources": Sources,
"Comic Book Lover": Comic_Book_Lover,
"File Rename": File_Rename,
"Auto-Tag": Auto_Tag,
"General": General,
"Dialog Flags": Dialog_Flags,
"Archive": Archive,
"Source comicvine": Source_comicvine,
},
)

View File

@ -29,9 +29,11 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
import comictaggerlib.ui.talkeruigenerator
from comicapi import utils
from comicapi.archivers.archiver import Archiver
from comicapi.genericmetadata import md_test
from comictaggerlib import ctsettings
from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.ctsettings.plugin import group_for_plugin
from comictaggerlib.filerenamer import FileRenamer, Replacement, Replacements
from comictaggerlib.ui import ui_path
from comictalker.comictalker import ComicTalker
@ -155,7 +157,6 @@ class SettingsWindow(QtWidgets.QDialog):
self.lblRarHelp.setText(linuxRarHelp)
elif platform.system() == "Darwin":
self.leRarExePath.setReadOnly(False)
self.lblRarHelp.setText(macRarHelp)
self.name = "Preferences"
@ -367,8 +368,9 @@ class SettingsWindow(QtWidgets.QDialog):
def settings_to_form(self) -> None:
self.disconnect_signals()
# Copy values from settings to form
if "archiver" in self.config[1] and "rar" in self.config[1]["archiver"].v:
self.leRarExePath.setText(getattr(self.config[0], self.config[1]["archiver"].v["rar"].internal_name))
archive_group = group_for_plugin(Archiver)
if archive_group in self.config[1] and "rar" in self.config[1][archive_group].v:
self.leRarExePath.setText(getattr(self.config[0], self.config[1][archive_group].v["rar"].internal_name))
else:
self.leRarExePath.setEnabled(False)
self.sbNameMatchIdentifyThresh.setValue(self.config[0].Issue_Identifier__series_match_identify_thresh)
@ -482,11 +484,12 @@ class SettingsWindow(QtWidgets.QDialog):
)
# Copy values from form to settings and save
if "archiver" in self.config[1] and "rar" in self.config[1]["archiver"].v:
setattr(self.config[0], self.config[1]["archiver"].v["rar"].internal_name, str(self.leRarExePath.text()))
archive_group = group_for_plugin(Archiver)
if archive_group in self.config[1] and "rar" in self.config[1][archive_group].v:
setattr(self.config[0], self.config[1][archive_group].v["rar"].internal_name, str(self.leRarExePath.text()))
# make sure rar program is now in the path for the rar class
if self.config[0].archiver_rar: # type: ignore[attr-defined]
if self.config[0].Archive__rar:
utils.add_to_path(os.path.dirname(str(self.leRarExePath.text())))
if not str(self.leIssueNumPadding.text()).isdigit():

View File

@ -1044,6 +1044,9 @@ class TaggerWindow(QtWidgets.QMainWindow):
dialog.setNameFilters(filters)
dialog.setFileMode(QtWidgets.QFileDialog.FileMode.ExistingFiles)
if os.environ.get("XDG_SESSION_DESKTOP", "") == "KDE":
dialog.setOption(QtWidgets.QFileDialog.Option.DontUseNativeDialog)
if self.config[0].internal__last_opened_folder is not None:
dialog.setDirectory(self.config[0].internal__last_opened_folder)
return dialog
@ -1144,40 +1147,44 @@ class TaggerWindow(QtWidgets.QMainWindow):
def commit_metadata(self) -> None:
if self.metadata is not None and self.comic_archive is not None:
reply = QtWidgets.QMessageBox.question(
self,
"Save Tags",
f"Are you sure you wish to save {', '.join([metadata_styles[style].name() for style in self.save_data_styles])} tags to this archive?",
QtWidgets.QMessageBox.StandardButton.Yes,
QtWidgets.QMessageBox.StandardButton.No,
)
if self.config[0].General__prompt_on_save:
reply = QtWidgets.QMessageBox.question(
self,
"Save Tags",
f"Are you sure you wish to save {', '.join([metadata_styles[style].name() for style in self.save_data_styles])} tags to this archive?",
QtWidgets.QMessageBox.StandardButton.Yes,
QtWidgets.QMessageBox.StandardButton.No,
)
else:
reply = QtWidgets.QMessageBox.StandardButton.Yes
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
self.form_to_metadata()
if reply != QtWidgets.QMessageBox.StandardButton.Yes:
return
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
self.form_to_metadata()
failed_style: str = ""
# Save each style
for style in self.save_data_styles:
success = self.comic_archive.write_metadata(self.metadata, style)
if not success:
failed_style = metadata_styles[style].name()
break
failed_style: str = ""
# Save each style
for style in self.save_data_styles:
success = self.comic_archive.write_metadata(self.metadata, style)
if not success:
failed_style = metadata_styles[style].name()
break
self.comic_archive.load_cache(list(metadata_styles))
QtWidgets.QApplication.restoreOverrideCursor()
self.comic_archive.load_cache(list(metadata_styles))
QtWidgets.QApplication.restoreOverrideCursor()
if failed_style:
QtWidgets.QMessageBox.warning(
self,
"Save failed",
f"The tag save operation seemed to fail for: {failed_style}",
)
else:
self.clear_dirty_flag()
self.update_info_box()
self.update_menus()
self.fileSelectionList.update_current_row()
if failed_style:
QtWidgets.QMessageBox.warning(
self,
"Save failed",
f"The tag save operation seemed to fail for: {failed_style}",
)
else:
self.clear_dirty_flag()
self.update_info_box()
self.update_menus()
self.fileSelectionList.update_current_row()
self.metadata = self.comic_archive.read_metadata(self.load_data_style)
self.update_ui_for_archive()

View File

@ -41,7 +41,24 @@
<string/>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="2" column="0">
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="cbxCheckForNewVersion">
<property name="text">
<string>Check for new version on startup</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxShortMetadataNames">
<property name="toolTip">
<string>Use the short name for the metadata styles (CBI, CR, etc.)</string>
</property>
<property name="text">
<string>Use &quot;short&quot; names for metadata styles</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="btnResetSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -54,7 +71,7 @@
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QPushButton" name="btnClearCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -68,22 +85,6 @@
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>If you need to free up the disk space, or the responses seems out of date, clear the online cache.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblDefaultSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -99,20 +100,26 @@
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QCheckBox" name="cbxCheckForNewVersion">
<item row="4" column="1">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Check for new version on startup</string>
<string>If you need to free up the disk space, or the responses seems out of date, clear the online cache.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxShortMetadataNames">
<property name="toolTip">
<string>Use the short name for the metadata styles (CBI, CR, etc.)</string>
</property>
<item row="2" column="0">
<widget class="QCheckBox" name="cbxPromptOnSave">
<property name="text">
<string>Use &quot;short&quot; names for metadata styles</string>
<string>Prompts the user to confirm saving tags</string>
</property>
</widget>
</item>
@ -780,7 +787,7 @@
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>

View File

@ -927,6 +927,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0">
@ -941,6 +944,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
@ -1053,6 +1059,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item row="2" column="0">
@ -1079,6 +1088,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
@ -1105,6 +1117,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
<item row="4" column="0">
@ -1137,6 +1152,9 @@
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="acceptRichText">
<bool>false</bool>
</property>
</widget>
</item>
</layout>

View File

@ -46,7 +46,7 @@ install_requires =
pyrate-limiter>=2.6,<3
rapidfuzz>=2.12.0
requests==2.*
settngs==0.9.3
settngs==0.10.0
text2digits
typing-extensions>=4.3.0
wordninja