Add OverlayMode options for read style and data source

This commit is contained in:
Mizaki 2024-02-04 20:33:10 +00:00
parent 851339d4e3
commit 8b0683f67c
13 changed files with 905 additions and 286 deletions

View File

@ -24,6 +24,7 @@ from __future__ import annotations
import copy
import dataclasses
import logging
import sys
from collections.abc import Sequence
from enum import Enum, auto
from typing import TYPE_CHECKING, Any, TypedDict, Union
@ -32,6 +33,7 @@ from typing_extensions import NamedTuple, Required
from comicapi import utils
from comicapi._url import Url, parse_url
from comicapi.utils import norm_fold
if TYPE_CHECKING:
Union
@ -46,6 +48,45 @@ class __remove(Enum):
REMOVE = __remove.REMOVE
if sys.version_info < (3, 11):
class StrEnum(str, Enum):
"""
Enum where members are also (and must be) strings
"""
def __new__(cls, *values: Any) -> Any:
"values must already be of type `str`"
if len(values) > 3:
raise TypeError(f"too many arguments for str(): {values!r}")
if len(values) == 1:
# it must be a string
if not isinstance(values[0], str):
raise TypeError(f"{values[0]!r} is not a string")
if len(values) >= 2:
# check that encoding argument is a string
if not isinstance(values[1], str):
raise TypeError(f"encoding must be a string, not {values[1]!r}")
if len(values) == 3:
# check that errors argument is a string
if not isinstance(values[2], str):
raise TypeError("errors must be a string, not %r" % (values[2]))
value = str(*values)
member = str.__new__(cls, value)
member._value_ = value
return member
@staticmethod
def _generate_next_value_(name: str, start: int, count: int, last_values: Any) -> str:
"""
Return the lower-cased version of the member name.
"""
return name.lower()
else:
from enum import StrEnum
class PageType:
"""
These page info classes are exactly the same as the CIX scheme, since
@ -104,6 +145,12 @@ class TagOrigin(NamedTuple):
name: str
class OverlayMode(StrEnum):
overlay = auto()
add_missing = auto()
combine = auto()
@dataclasses.dataclass
class GenericMetadata:
writer_synonyms = ("writer", "plotter", "scripter", "script")
@ -214,101 +261,172 @@ class GenericMetadata:
new_md.__post_init__()
return new_md
def overlay(self, new_md: GenericMetadata) -> None:
"""Overlay a metadata object on this one
def credit_dedupe(self, cur: list[Credit], new: list[Credit]) -> list[Credit]:
if len(new) == 0:
return cur
if len(cur) == 0:
return new
That is, when the new object has non-None values, over-write them
to this one.
"""
# Create dict for deduplication
new_dict: dict[str, Credit] = {f"{norm_fold(n['person'])}_{n['role'].casefold()}": n for n in new}
cur_dict: dict[str, Credit] = {f"{norm_fold(c['person'])}_{c['role'].casefold()}": c for c in cur}
def assign(cur: str, new: Any) -> None:
if new is not None:
if new is REMOVE:
if isinstance(getattr(self, cur), (list, set)):
getattr(self, cur).clear()
else:
setattr(self, cur, None)
return
# Any duplicates use the 'new' value
cur_dict.update(new_dict)
return list(cur_dict.values())
if isinstance(new, str) and len(new) == 0:
setattr(self, cur, None)
elif isinstance(new, (list, set)) and len(new) == 0:
pass
def assign_dedupe(self, new: list[str] | set[str], cur: list[str] | set[str]) -> list[str] | set[str]:
"""Dedupes normalised (NFKD), casefolded values using 'new' values on collisions"""
if len(new) == 0:
return cur
if len(cur) == 0:
return new
# Create dict values for deduplication
new_dict: dict[str, str] = {norm_fold(n): n for n in new}
cur_dict: dict[str, str] = {norm_fold(c): c for c in cur}
if isinstance(cur, list):
cur_dict.update(new_dict)
return list(cur_dict.values())
if isinstance(cur, set):
cur_dict.update(new_dict)
return set(cur_dict.values())
# All else fails
return cur
def assign_overlay(self, cur: Any, new: Any) -> Any:
"""Overlay - When the 'new' object has non-None values, overwrite 'cur'(rent) with 'new'."""
if new is None:
return cur
if isinstance(new, (list, set)) and len(new) == 0:
return cur
else:
return new
def assign_add_missing(self, cur: Any, new: Any) -> Any:
"""Add Missing - Any 'cur(rent)' values that are None or an empty list/set, add 'new' non-None values"""
if new is None:
return cur
if cur is None:
return new
elif isinstance(cur, (list, set)) and len(cur) == 0:
return new
else:
return cur
def assign_combine(self, cur: Any, new: Any) -> Any:
"""Combine - Combine lists and sets (checking for dupes). All non-None values from new replace cur(rent)"""
if new is None:
return cur
if isinstance(new, (list, set)) and isinstance(cur, (list, set)):
# Check for weblinks (Url is a tuple)
if len(new) > 0 and isinstance(new, list) and isinstance(new[0], Url):
return list(set(new).union(cur))
return self.assign_dedupe(new, cur)
else:
return new
def overlay(self, new_md: GenericMetadata, mode: OverlayMode = OverlayMode.overlay) -> None:
"""Overlay a new metadata object on this one"""
assign_funcs = {
OverlayMode.overlay: self.assign_overlay,
OverlayMode.add_missing: self.assign_add_missing,
OverlayMode.combine: self.assign_combine,
}
def assign(cur: Any, new: Any) -> Any:
if new is REMOVE:
if isinstance(cur, (list, set)):
cur.clear()
return cur
else:
setattr(self, cur, new)
return None
assign_func = assign_funcs.get(mode, self.assign_overlay)
return assign_func(cur, new)
if not new_md.is_empty:
self.is_empty = False
assign("tag_origin", new_md.tag_origin)
assign("issue_id", new_md.issue_id)
assign("series_id", new_md.series_id)
self.tag_origin = assign(self.tag_origin, new_md.tag_origin) # TODO use and purpose now?
self.issue_id = assign(self.issue_id, new_md.issue_id)
self.series_id = assign(self.series_id, new_md.series_id)
assign("series", new_md.series)
assign("series_aliases", new_md.series_aliases)
assign("issue", new_md.issue)
assign("issue_count", new_md.issue_count)
assign("title", new_md.title)
assign("title_aliases", new_md.title_aliases)
assign("volume", new_md.volume)
assign("volume_count", new_md.volume_count)
assign("genres", new_md.genres)
assign("description", new_md.description)
assign("notes", new_md.notes)
self.series = assign(self.series, new_md.series)
self.series_aliases = assign(self.series_aliases, new_md.series_aliases)
self.issue = assign(self.issue, new_md.issue)
self.issue_count = assign(self.issue_count, new_md.issue_count)
self.title = assign(self.title, new_md.title)
self.title_aliases = assign(self.title_aliases, new_md.title_aliases)
self.volume = assign(self.volume, new_md.volume)
self.volume_count = assign(self.volume_count, new_md.volume_count)
self.genres = assign(self.genres, new_md.genres)
self.description = assign(self.description, new_md.description)
self.notes = assign(self.notes, new_md.notes)
assign("alternate_series", new_md.alternate_series)
assign("alternate_number", new_md.alternate_number)
assign("alternate_count", new_md.alternate_count)
assign("story_arcs", new_md.story_arcs)
assign("series_groups", new_md.series_groups)
self.alternate_series = assign(self.alternate_series, new_md.alternate_series)
self.alternate_number = assign(self.alternate_number, new_md.alternate_number)
self.alternate_count = assign(self.alternate_count, new_md.alternate_count)
self.story_arcs = assign(self.story_arcs, new_md.story_arcs)
self.series_groups = assign(self.series_groups, new_md.series_groups)
assign("publisher", new_md.publisher)
assign("imprint", new_md.imprint)
assign("day", new_md.day)
assign("month", new_md.month)
assign("year", new_md.year)
assign("language", new_md.language)
assign("country", new_md.country)
assign("web_links", new_md.web_links)
assign("format", new_md.format)
assign("manga", new_md.manga)
assign("black_and_white", new_md.black_and_white)
assign("maturity_rating", new_md.maturity_rating)
assign("critical_rating", new_md.critical_rating)
assign("scan_info", new_md.scan_info)
self.publisher = assign(self.publisher, new_md.publisher)
self.imprint = assign(self.imprint, new_md.imprint)
self.day = assign(self.day, new_md.day)
self.month = assign(self.month, new_md.month)
self.year = assign(self.year, new_md.year)
self.language = assign(self.language, new_md.language)
self.country = assign(self.country, new_md.country)
self.web_links = assign(self.web_links, new_md.web_links)
self.format = assign(self.format, new_md.format)
self.manga = assign(self.manga, new_md.manga)
self.black_and_white = assign(self.black_and_white, new_md.black_and_white)
self.maturity_rating = assign(self.maturity_rating, new_md.maturity_rating)
self.critical_rating = assign(self.critical_rating, new_md.critical_rating)
self.scan_info = assign(self.scan_info, new_md.scan_info)
assign("tags", new_md.tags)
assign("pages", new_md.pages)
assign("page_count", new_md.page_count)
self.tags = assign(self.tags, new_md.tags)
self.pages = assign(self.pages, new_md.pages)
self.page_count = assign(self.page_count, new_md.page_count)
assign("characters", new_md.characters)
assign("teams", new_md.teams)
assign("locations", new_md.locations)
self.overlay_credits(new_md.credits)
self.characters = assign(self.characters, new_md.characters)
self.teams = assign(self.teams, new_md.teams)
self.locations = assign(self.locations, new_md.locations)
self.credits = self.assign_credits(self.credits, new_md.credits)
assign("price", new_md.price)
assign("is_version_of", new_md.is_version_of)
assign("rights", new_md.rights)
assign("identifier", new_md.identifier)
assign("last_mark", new_md.last_mark)
assign("_cover_image", new_md._cover_image)
assign("_alternate_images", new_md._alternate_images)
self.price = assign(self.price, new_md.price)
self.is_version_of = assign(self.is_version_of, new_md.is_version_of)
self.rights = assign(self.rights, new_md.rights)
self.identifier = assign(self.identifier, new_md.identifier)
self.last_mark = assign(self.last_mark, new_md.last_mark)
self._cover_image = assign(self._cover_image, new_md._cover_image)
self._alternate_images = assign(self._alternate_images, new_md._alternate_images)
def overlay_credits(self, new_credits: list[Credit]) -> None:
def assign_credits_overlay(self, cur_credits: list[Credit], new_credits: list[Credit]) -> list[Credit]:
return self.credit_dedupe(cur_credits, new_credits)
def assign_credits_add_missing(self, cur_credits: list[Credit], new_credits: list[Credit]) -> list[Credit]:
# Send new_credits as cur_credits and vis-versa to keep cur_credit on duplication
return self.credit_dedupe(new_credits, cur_credits)
def assign_credits(
self, cur_credits: list[Credit], new_credits: list[Credit], mode: OverlayMode = OverlayMode.overlay
) -> list[Credit]:
if new_credits is REMOVE:
self.credits = []
return
for c in new_credits:
primary = bool("primary" in c and c["primary"])
return []
# Remove credit role if person is blank
if c["person"] == "":
for r in reversed(self.credits):
if r["role"].casefold() == c["role"].casefold():
self.credits.remove(r)
# otherwise, add it!
else:
self.add_credit(c["person"], c["role"], primary)
assign_credit_funcs = {
OverlayMode.overlay: self.assign_credits_overlay,
OverlayMode.add_missing: self.assign_credits_add_missing,
OverlayMode.combine: self.assign_credits_overlay,
}
assign_credit_func = assign_credit_funcs.get(mode, self.assign_credits_overlay)
return assign_credit_func(cur_credits, new_credits)
def apply_default_page_list(self, page_list: Sequence[str]) -> None:
# generate a default page list, with the first page marked as the cover
@ -354,12 +472,15 @@ class GenericMetadata:
return coverlist
def add_credit(self, person: str, role: str, primary: bool = False) -> None:
if person == "":
return
credit = Credit(person=person, role=role, primary=primary)
# look to see if it's not already there...
found = False
for c in self.credits:
if c["person"].casefold() == person.casefold() and c["role"].casefold() == role.casefold():
if norm_fold(c["person"]) == norm_fold(person) and norm_fold(c["role"]) == norm_fold(role):
# no need to add it. just adjust the "primary" flag as needed
c["primary"] = primary
found = True

View File

@ -223,6 +223,11 @@ def parse_filename(
return fni
def norm_fold(string: str) -> str:
"""Normalise and casefold string"""
return unicodedata.normalize("NFKD", string).casefold()
def combine_notes(existing_notes: str | None, new_notes: str | None, split: str) -> str:
split_notes, split_str, untouched_notes = (existing_notes or "").rpartition(split)
if split_notes or split_str:

View File

@ -81,7 +81,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
self.match_set_list = match_set_list
self.load_data_styles = load_styles
self._styles = styles
self.fetch_func = fetch_func
self.current_match_set_idx = 0
@ -262,8 +262,8 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
return
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
md.overlay(ct_md)
for style in self.load_data_styles:
md.overlay(ct_md, self.config.Metadata_Options__source_overlay)
for style in self._styles:
success = ca.write_metadata(md, style)
QtWidgets.QApplication.restoreOverrideCursor()
if not success:

View File

@ -30,7 +30,7 @@ class CBLTransformer:
self.config = config
def apply(self) -> GenericMetadata:
if self.config.Comic_Book_Lover__assume_lone_credit_is_primary:
if self.config.Metadata_Options__cbl_assume_lone_credit_is_primary:
# helper
def set_lone_primary(role_list: list[str]) -> tuple[Credit | None, int]:
lone_credit: Credit | None = None
@ -56,19 +56,19 @@ class CBLTransformer:
c["primary"] = False
self.metadata.add_credit(c["person"], "Artist", True)
if self.config.Comic_Book_Lover__copy_characters_to_tags:
if self.config.Metadata_Options__cbl_copy_characters_to_tags:
self.metadata.tags.update(x for x in self.metadata.characters)
if self.config.Comic_Book_Lover__copy_teams_to_tags:
if self.config.Metadata_Options__cbl_copy_teams_to_tags:
self.metadata.tags.update(x for x in self.metadata.teams)
if self.config.Comic_Book_Lover__copy_locations_to_tags:
if self.config.Metadata_Options__cbl_copy_locations_to_tags:
self.metadata.tags.update(x for x in self.metadata.locations)
if self.config.Comic_Book_Lover__copy_storyarcs_to_tags:
if self.config.Metadata_Options__cbl_copy_storyarcs_to_tags:
self.metadata.tags.update(x for x in self.metadata.story_arcs)
if self.config.Comic_Book_Lover__copy_notes_to_comments:
if self.config.Metadata_Options__cbl_copy_notes_to_comments:
if self.metadata.notes is not None:
if self.metadata.description is None:
self.metadata.description = ""
@ -77,7 +77,7 @@ class CBLTransformer:
if self.metadata.notes not in self.metadata.description:
self.metadata.description += self.metadata.notes
if self.config.Comic_Book_Lover__copy_weblink_to_comments:
if self.config.Metadata_Options__cbl_copy_weblink_to_comments:
for web_link in self.metadata.web_links:
temp_desc = self.metadata.description
if temp_desc is None:

View File

@ -127,7 +127,7 @@ class CLI:
logger.exception(f"Error retrieving issue details. Save aborted.\n{e}")
return GenericMetadata()
if self.config.Comic_Book_Lover__apply_transform_on_import:
if self.config.Metadata_Options__cbl_apply_transform_on_import:
ct_md = CBLTransformer(ct_md, self.config).apply()
return ct_md
@ -253,11 +253,12 @@ class CLI:
if ca.has_metadata(style):
try:
t_md = ca.read_metadata(style)
md.overlay(t_md)
md.overlay(t_md, self.config.Metadata_Options__read_style_overlay)
break
except Exception as e:
logger.error("Failed to load metadata for %s: %s", ca.path, e)
# finally, use explicit stuff
# finally, use explicit stuff (always 'overlay' mode)
md.overlay(self.config.Runtime_Options__metadata)
return md
@ -340,7 +341,7 @@ class CLI:
src_style_name = md_styles[self.config.Commands__copy].name()
if not self.config.Runtime_Options__dryrun:
if self.config.Comic_Book_Lover__apply_transform_on_bulk_operation == "cbi":
if self.config.Metadata_Options__cbl_apply_transform_on_bulk_operation == "cbi":
md = CBLTransformer(md, self.config).apply()
if ca.write_metadata(md, style):

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import json
import logging
import pathlib
from enum import Enum
from typing import Any
import settngs
@ -55,7 +56,11 @@ def validate_types(config: settngs.Config[settngs.Values]) -> settngs.Config[set
if setting.type is not None:
# If it is not the default and the type attribute is not None
# use it to convert the loaded string into the expected value
if isinstance(value, str):
if (
isinstance(value, str)
or isinstance(default, Enum)
or (isinstance(setting.type, type) and issubclass(setting.type, Enum))
):
config.values[setting.group][setting.dest] = setting.type(value)
return config

View File

@ -6,6 +6,7 @@ import uuid
import settngs
from comicapi import utils
from comicapi.genericmetadata import OverlayMode
from comictaggerlib.ctsettings.settngs_namespace import SettngsNS as ct_ns
from comictaggerlib.defaults import DEFAULT_REPLACEMENTS, Replacement, Replacements
@ -13,13 +14,6 @@ from comictaggerlib.defaults import DEFAULT_REPLACEMENTS, Replacement, Replaceme
def general(parser: settngs.Manager) -> None:
# General Settings
parser.add_setting("check_for_new_version", default=False, cmdline=False)
parser.add_setting(
"--disable-cr",
default=False,
action=argparse.BooleanOptionalAction,
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,
@ -164,17 +158,38 @@ def talker(parser: settngs.Manager) -> None:
)
def cbl(parser: settngs.Manager) -> None:
def md_options(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)
parser.add_setting("--copy-teams-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-locations-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-storyarcs-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-notes-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--copy-weblink-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-transform-on-import", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--apply-transform-on-bulk-operation", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-assume-lone-credit-is-primary", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-characters-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-teams-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-locations-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-storyarcs-to-tags", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-notes-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-copy-weblink-to-comments", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-apply-transform-on-import", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting("--cbl-apply-transform-on-bulk-operation", default=False, action=argparse.BooleanOptionalAction)
parser.add_setting(
"--read-style-overlay",
default=OverlayMode.overlay,
type=OverlayMode,
help="How to overlay new metadata on the current for enabled read styles (CR, CBL, etc.)",
)
parser.add_setting(
"--source-overlay",
default=OverlayMode.overlay,
type=OverlayMode,
help="How to overlay the new metadata from a source (CV, Metron, GCD, etc.) on to the current",
)
parser.add_setting("use_short_metadata_names", default=False, action=argparse.BooleanOptionalAction, cmdline=False)
parser.add_setting(
"--disable-cr",
default=False,
action=argparse.BooleanOptionalAction,
help="Disable the ComicRack metadata type",
)
def rename(parser: settngs.Manager) -> None:
@ -311,7 +326,7 @@ def register_file_settings(parser: settngs.Manager) -> None:
parser.add_group("Issue Identifier", identifier, False)
parser.add_group("Filename Parsing", filename, False)
parser.add_group("Sources", talker, False)
parser.add_group("Comic Book Lover", cbl, False)
parser.add_group("Metadata Options", md_options, False)
parser.add_group("File Rename", rename, False)
parser.add_group("Auto-Tag", autotag, False)
parser.add_group("General", general, False)

View File

@ -9,6 +9,7 @@ import comicapi.utils
import comictaggerlib.ctsettings.types
import comictaggerlib.defaults
import comictaggerlib.resulttypes
from comicapi.genericmetadata import OverlayMode
class SettngsNS(settngs.TypedNS):
@ -74,15 +75,19 @@ class SettngsNS(settngs.TypedNS):
Sources__source: str
Sources__remove_html_tables: bool
Comic_Book_Lover__assume_lone_credit_is_primary: bool
Comic_Book_Lover__copy_characters_to_tags: bool
Comic_Book_Lover__copy_teams_to_tags: bool
Comic_Book_Lover__copy_locations_to_tags: bool
Comic_Book_Lover__copy_storyarcs_to_tags: bool
Comic_Book_Lover__copy_notes_to_comments: bool
Comic_Book_Lover__copy_weblink_to_comments: bool
Comic_Book_Lover__apply_transform_on_import: bool
Comic_Book_Lover__apply_transform_on_bulk_operation: bool
Metadata_Options__cbl_assume_lone_credit_is_primary: bool
Metadata_Options__cbl_copy_characters_to_tags: bool
Metadata_Options__cbl_copy_teams_to_tags: bool
Metadata_Options__cbl_copy_locations_to_tags: bool
Metadata_Options__cbl_copy_storyarcs_to_tags: bool
Metadata_Options__cbl_copy_notes_to_comments: bool
Metadata_Options__cbl_copy_weblink_to_comments: bool
Metadata_Options__cbl_apply_transform_on_import: bool
Metadata_Options__cbl_apply_transform_on_bulk_operation: bool
Metadata_Options__read_style_overlay: OverlayMode
Metadata_Options__source_overlay: OverlayMode
Metadata_Options__use_short_metadata_names: bool
Metadata_Options__disable_cr: bool
File_Rename__template: str
File_Rename__issue_number_padding: int
@ -101,8 +106,6 @@ class SettngsNS(settngs.TypedNS):
Auto_Tag__remove_archive_after_successful_match: bool
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
@ -189,16 +192,19 @@ class Sources(typing.TypedDict):
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 Metadata_Options(typing.TypedDict):
cbl_assume_lone_credit_is_primary: bool
cbl_copy_characters_to_tags: bool
cbl_copy_teams_to_tags: bool
cbl_copy_locations_to_tags: bool
cbl_copy_storyarcs_to_tags: bool
cbl_copy_notes_to_comments: bool
cbl_copy_weblink_to_comments: bool
cbl_apply_transform_on_import: bool
cbl_apply_transform_on_bulk_operation: bool
metadata_overlay: OverlayMode
use_short_metadata_names: bool
disable_cr: bool
class File_Rename(typing.TypedDict):
@ -223,8 +229,6 @@ class Auto_Tag(typing.TypedDict):
class General(typing.TypedDict):
check_for_new_version: bool
disable_cr: bool
use_short_metadata_names: bool
prompt_on_save: bool
@ -253,7 +257,7 @@ SettngsDict = typing.TypedDict(
"Issue Identifier": Issue_Identifier,
"Filename Parsing": Filename_Parsing,
"Sources": Sources,
"Comic Book Lover": Comic_Book_Lover,
"Metadata Options": Metadata_Options,
"File Rename": File_Rename,
"Auto-Tag": Auto_Tag,
"General": General,

View File

@ -252,7 +252,7 @@ class App:
# config already loaded
error = None
if self.config[0].General__disable_cr:
if self.config[0].Metadata_Options__disable_cr:
if "cr" in comicapi.comicarchive.metadata_styles:
del comicapi.comicarchive.metadata_styles["cr"]

View File

@ -30,7 +30,8 @@ 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
# TODO OverlayMode changed to merge
from comicapi.genericmetadata import OverlayMode, md_test
from comictaggerlib import ctsettings
from comictaggerlib.ctsettings import ct_ns
from comictaggerlib.ctsettings.plugin import group_for_plugin
@ -194,6 +195,10 @@ class SettingsWindow(QtWidgets.QDialog):
)
self.cbFilenameParser.clear()
self.cbFilenameParser.addItems(utils.Parser)
# TODO check
for mode in OverlayMode:
self.cbxOverlayReadStyle.addItem(mode.name.capitalize().replace("_", " "), mode.value)
self.cbxOverlaySource.addItem(mode.name.capitalize().replace("_", " "), mode.value)
self.connect_signals()
self.settings_to_form()
self.rename_test()
@ -399,7 +404,6 @@ class SettingsWindow(QtWidgets.QDialog):
self.tePublisherFilter.setPlainText("\n".join(self.config[0].Issue_Identifier__publisher_filter))
self.cbxCheckForNewVersion.setChecked(self.config[0].General__check_for_new_version)
self.cbxShortMetadataNames.setChecked(self.config[0].General__use_short_metadata_names)
self.cbFilenameParser.setCurrentText(self.config[0].Filename_Parsing__filename_parser)
self.cbxRemoveC2C.setChecked(self.config[0].Filename_Parsing__remove_c2c)
@ -417,17 +421,25 @@ class SettingsWindow(QtWidgets.QDialog):
self.cbxExactMatches.setChecked(self.config[0].Issue_Identifier__exact_series_matches_first)
self.cbxClearFormBeforePopulating.setChecked(self.config[0].Issue_Identifier__clear_metadata)
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.config[0].Comic_Book_Lover__assume_lone_credit_is_primary)
self.cbxCopyCharactersToTags.setChecked(self.config[0].Comic_Book_Lover__copy_characters_to_tags)
self.cbxCopyTeamsToTags.setChecked(self.config[0].Comic_Book_Lover__copy_teams_to_tags)
self.cbxCopyLocationsToTags.setChecked(self.config[0].Comic_Book_Lover__copy_locations_to_tags)
self.cbxCopyStoryArcsToTags.setChecked(self.config[0].Comic_Book_Lover__copy_storyarcs_to_tags)
self.cbxCopyNotesToComments.setChecked(self.config[0].Comic_Book_Lover__copy_notes_to_comments)
self.cbxCopyWebLinkToComments.setChecked(self.config[0].Comic_Book_Lover__copy_weblink_to_comments)
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.config[0].Comic_Book_Lover__apply_transform_on_import)
self.cbxAssumeLoneCreditIsPrimary.setChecked(self.config[0].Metadata_Options__cbl_assume_lone_credit_is_primary)
self.cbxCopyCharactersToTags.setChecked(self.config[0].Metadata_Options__cbl_copy_characters_to_tags)
self.cbxCopyTeamsToTags.setChecked(self.config[0].Metadata_Options__cbl_copy_teams_to_tags)
self.cbxCopyLocationsToTags.setChecked(self.config[0].Metadata_Options__cbl_copy_locations_to_tags)
self.cbxCopyStoryArcsToTags.setChecked(self.config[0].Metadata_Options__cbl_copy_storyarcs_to_tags)
self.cbxCopyNotesToComments.setChecked(self.config[0].Metadata_Options__cbl_copy_notes_to_comments)
self.cbxCopyWebLinkToComments.setChecked(self.config[0].Metadata_Options__cbl_copy_weblink_to_comments)
self.cbxApplyCBLTransformOnCVIMport.setChecked(self.config[0].Metadata_Options__cbl_apply_transform_on_import)
self.cbxApplyCBLTransformOnBatchOperation.setChecked(
self.config[0].Comic_Book_Lover__apply_transform_on_bulk_operation
self.config[0].Metadata_Options__cbl_apply_transform_on_bulk_operation
)
self.cbxOverlayReadStyle.setCurrentIndex(
self.cbxOverlayReadStyle.findData(self.config[0].Metadata_Options__read_style_overlay.value)
)
self.cbxOverlaySource.setCurrentIndex(
self.cbxOverlaySource.findData(self.config[0].Metadata_Options__source_overlay.value)
)
self.cbxShortMetadataNames.setChecked(self.config[0].Metadata_Options__use_short_metadata_names)
self.cbxDisableCR.setChecked(self.config[0].Metadata_Options__disable_cr)
self.leRenameTemplate.setText(self.config[0].File_Rename__template)
self.leIssueNumPadding.setText(str(self.config[0].File_Rename__issue_number_padding))
@ -544,18 +556,29 @@ class SettingsWindow(QtWidgets.QDialog):
self.config[0].Issue_Identifier__exact_series_matches_first = self.cbxExactMatches.isChecked()
self.config[0].Issue_Identifier__clear_metadata = self.cbxClearFormBeforePopulating.isChecked()
self.config[0].Comic_Book_Lover__assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()
self.config[0].Comic_Book_Lover__copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
self.config[0].Comic_Book_Lover__copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
self.config[0].Comic_Book_Lover__copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
self.config[0].Comic_Book_Lover__copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
self.config[0].Comic_Book_Lover__copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
self.config[0].Comic_Book_Lover__copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
self.config[0].Comic_Book_Lover__apply_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
self.config.values.Comic_Book_Lover__apply_transform_on_bulk_operation = (
self.config[0].Metadata_Options__cbl_assume_lone_credit_is_primary = (
self.cbxAssumeLoneCreditIsPrimary.isChecked()
)
self.config[0].Metadata_Options__cbl_copy_characters_to_tags = self.cbxCopyCharactersToTags.isChecked()
self.config[0].Metadata_Options__cbl_copy_teams_to_tags = self.cbxCopyTeamsToTags.isChecked()
self.config[0].Metadata_Options__cbl_copy_locations_to_tags = self.cbxCopyLocationsToTags.isChecked()
self.config[0].Metadata_Options__cbl_copy_storyarcs_to_tags = self.cbxCopyStoryArcsToTags.isChecked()
self.config[0].Metadata_Options__cbl_copy_notes_to_comments = self.cbxCopyNotesToComments.isChecked()
self.config[0].Metadata_Options__cbl_copy_weblink_to_comments = self.cbxCopyWebLinkToComments.isChecked()
self.config[0].Metadata_Options__cbl_apply_transform_on_import = self.cbxApplyCBLTransformOnCVIMport.isChecked()
self.config.values.Metadata_Options__cbl_apply_transform_on_bulk_operation = (
self.cbxApplyCBLTransformOnBatchOperation.isChecked()
)
self.config[0].Metadata_Options__read_style_overlay = OverlayMode[self.cbxOverlayReadStyle.currentData()]
self.config[0].Metadata_Options__source_overlay = OverlayMode[self.cbxOverlaySource.currentData()]
self.config[0].Metadata_Options__disable_cr = self.cbxDisableCR.isChecked()
# Update metadata style names if required
if self.config[0].Metadata_Options__use_short_metadata_names != self.cbxShortMetadataNames.isChecked():
self.config[0].Metadata_Options__use_short_metadata_names = self.cbxShortMetadataNames.isChecked()
self.parent().populate_style_names()
self.parent().adjust_save_style_combo()
self.config[0].File_Rename__template = str(self.leRenameTemplate.text())
self.config[0].File_Rename__issue_number_padding = int(self.leIssueNumPadding.text())
self.config[0].File_Rename__use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()

View File

@ -1431,7 +1431,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.cbLoadDataStyle.clear()
# Add the entries to the tag style combobox
for style in metadata_styles.values():
if self.config[0].General__use_short_metadata_names:
if self.config[0].Metadata_Options__use_short_metadata_names:
self.cbSaveDataStyle.addItem(style.short_name.upper(), style.short_name)
self.cbLoadDataStyle.addItem(style.short_name.upper(), style.short_name)
else:
@ -1701,7 +1701,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
center_window_on_parent(prog_dialog)
QtCore.QCoreApplication.processEvents()
if style == "cbi" and self.config[0].Comic_Book_Lover__apply_transform_on_bulk_operation:
if style == "cbi" and self.config[0].Metadata_Options__cbl_apply_transform_on_bulk_operation:
md = CBLTransformer(md, self.config[0]).apply()
if ca.write_metadata(md, style):

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>703</width>
<height>574</height>
<height>597</height>
</rect>
</property>
<property name="windowTitle">
@ -28,7 +28,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
<number>4</number>
</property>
<widget class="QWidget" name="tGeneral">
<attribute name="title">
@ -48,43 +48,7 @@
</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">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Default Settings</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QPushButton" name="btnClearCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear Cache</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="2" column="1">
<widget class="QLabel" name="lblDefaultSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -103,7 +67,7 @@
</property>
</widget>
</item>
<item row="4" column="1">
<item row="3" column="1">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
@ -122,13 +86,39 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="cbxPromptOnSave">
<property name="text">
<string>Prompts the user to confirm saving tags</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="btnResetSettings">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Default Settings</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QPushButton" name="btnClearCache">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Clear Cache</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -427,19 +417,124 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_4"/>
</widget>
<widget class="QWidget" name="tMDType">
<widget class="QWidget" name="tMDOptions">
<attribute name="title">
<string>Metadata Types</string>
<string>Metadata Options</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_6">
<item row="1" column="0">
<widget class="QCheckBox" name="cbxApplyCBLTransformOnBatchOperation">
<property name="text">
<string>Apply CBL Transforms on Batch Copy Operations to CBL Tags</string>
<item row="0" column="0" rowspan="4">
<widget class="QGroupBox" name="groupBox_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxShortMetadataNames">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<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="0" column="1">
<widget class="QCheckBox" name="cbxDisableCR">
<property name="toolTip">
<string>Useful if you use the CIX metadata type</string>
</property>
<property name="text">
<string>Disable ComicRack Metadata Type</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0" rowspan="2">
<widget class="QGroupBox" name="groupBox_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>80</height>
</size>
</property>
<property name="title">
<string>CBL Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<item>
<widget class="QCheckBox" name="cbxApplyCBLTransformOnCVIMport">
<property name="text">
<string>Apply CBL Transforms on ComicVine Import</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxApplyCBLTransformOnBatchOperation">
<property name="text">
<string>Apply CBL Transforms on Batch Copy Operations to CBL Tags</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
@ -450,99 +545,347 @@
<property name="title">
<string>CBL Transforms</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>11</x>
<y>21</y>
<width>251</width>
<height>206</height>
</rect>
<layout class="QGridLayout" name="gridLayout_9">
<property name="leftMargin">
<number>6</number>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<item row="3" column="0">
<widget class="QCheckBox" name="cbxCopyLocationsToTags">
<property name="text">
<string>Copy Locations to Generic Tags</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxAssumeLoneCreditIsPrimary">
<property name="text">
<string>Assume Lone Credit Is Primary</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxCopyCharactersToTags">
<property name="text">
<string>Copy Characters to Generic Tags</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="cbxCopyTeamsToTags">
<property name="text">
<string>Copy Teams to Generic Tags</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="cbxCopyNotesToComments">
<property name="text">
<string>Copy Notes to Comments</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QCheckBox" name="cbxCopyWebLinkToComments">
<property name="text">
<string>Copy Web Link to Comments</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="cbxCopyStoryArcsToTags">
<property name="text">
<string>Copy Story Arcs to Generic Tags</string>
</property>
</widget>
</item>
</layout>
</widget>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="1">
<widget class="QCheckBox" name="cbxCopyNotesToComments">
<property name="text">
<string>Copy Notes to Comments</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="cbxCopyWebLinkToComments">
<property name="text">
<string>Copy Web Link to Comments</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="cbxCopyLocationsToTags">
<property name="text">
<string>Copy Locations to Generic Tags</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxAssumeLoneCreditIsPrimary">
<property name="text">
<string>Assume Lone Credit Is Primary</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="cbxCopyStoryArcsToTags">
<property name="text">
<string>Copy Story Arcs to Generic Tags</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="cbxCopyCharactersToTags">
<property name="text">
<string>Copy Characters to Generic Tags</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="cbxCopyTeamsToTags">
<property name="text">
<string>Copy Teams to Generic Tags</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QGroupBox" name="groupBox_6">
<property name="minimumSize">
<size>
<width>0</width>
<height>165</height>
</size>
</property>
<property name="title">
<string>Overlay</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>6</number>
</property>
<property name="rightMargin">
<number>6</number>
</property>
<property name="bottomMargin">
<number>6</number>
</property>
<property name="spacing">
<number>6</number>
</property>
<item row="1" column="0">
<widget class="QLabel" name="lblOverlaySource">
<property name="toolTip">
<string>The operation to perform when overlaying source data (Comic Vine, Metron, GCD, etc.)</string>
</property>
<property name="text">
<string>Data Source</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="margin">
<number>6</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblOverlayReadStyle">
<property name="toolTip">
<string>The operation to perform when overlaying read styles (ComicRack, ComicBookInfo, etc.)</string>
</property>
<property name="text">
<string>Read Style</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="margin">
<number>6</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="cbxOverlaySource">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>The operation to perform when overlaying source data (Comic Vine, Metron, GCD, etc.)</string>
</property>
</widget>
</item>
<item row="0" column="2" rowspan="3">
<widget class="QTabWidget" name="tabWidgetOverlay">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>200</height>
</size>
</property>
<property name="tabPosition">
<enum>QTabWidget::North</enum>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<property name="documentMode">
<bool>true</bool>
</property>
<property name="tabBarAutoHide">
<bool>false</bool>
</property>
<widget class="QWidget" name="tOverlay">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<attribute name="title">
<string>Overlay</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPlainTextEdit" name="overlayTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="undoRedoEnabled">
<bool>false</bool>
</property>
<property name="plainText">
<string>Read Style: Overlays all the none empty values of the read style metadata on top of the current metadata (i.e. file name parsing, other read style(s)).
Data Source: Overlays all the none empty values from the data source (e.g. Comic Vine) on top of the current metadata (i.e. file name parsing, read style(s)).
Example:
(Series=batman, Issue=1, Tags=[batman,joker,robin])
+ (Series=Batman, Publisher=DC Comics, Tags=[mystery,action])
= (Series=Batman, Issue=1, Publisher=DC Comics, Tags=[mystery,action])</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tAdd">
<attribute name="title">
<string>Add Missing</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPlainTextEdit" name="addTextEdit">
<property name="plainText">
<string>Read Style: Adds any metadata that is present in the read style but is missing in the current metadata (i.e. file name parsing, other read style(s)).
Data Source: Add any metadata that is present in the data source (e.g. Comic Vine) but is missing in the current metadata (i.e. file name parsing, read style(s)).
Example:
(Series=batman, Issue=1, Tags=[batman,joker,robin])
+ (Series=Superman, Issue=10, Publisher=DC Comics, Tags=[superman,lois lane,lex luthor])
= (Series=batman, Issue=1, Publisher=DC Comics, Tags=[batman,joker,robin])</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tCombine">
<attribute name="title">
<string>Combine</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_10">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QPlainTextEdit" name="combineTextEdit">
<property name="plainText">
<string>Read Style: Same as Overlay except lists (tags, characters, genres, credits, etc.) are combined with the current metadata (i.e. file name parsing, other read style(s)).
Data Source: Same as Overlay except lists (tags, characters, genres, credits, etc.) are combined with the current metadata (i.e. file name parsing, read style(s)).
Example:
(Series=batman, Issue=1, Tags=[batman,joker,robin])
+ (Series=Batman, Publisher=DC Comics, Tags=[mystery,action])
= (Series=Batman, Issue=1, Publisher=DC Comics, Tags=[batman,joker,robin,mystery,action])</string>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="cbxOverlayReadStyle">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>The operation to perform when overlaying read styles (ComicRack, ComicBookInfo, etc.)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>10</height>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="cbxApplyCBLTransformOnCVIMport">
<property name="text">
<string>Apply CBL Transforms on ComicVine Import</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="cbxDisableCR">
<property name="text">
<string>Disable ComicRack Metadata Type (useful if you use the CIX metadata type)</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tRename">

View File

@ -5,6 +5,7 @@ import textwrap
import pytest
import comicapi.genericmetadata
from comicapi.genericmetadata import OverlayMode, parse_url
from testing.comicdata import credits, metadata
@ -24,6 +25,107 @@ def test_metadata_overlay(md: comicapi.genericmetadata.GenericMetadata, replaced
assert md == expected
def test_metadata_overlay_add_missing():
md = comicapi.genericmetadata.GenericMetadata(series="test", issue="1", title="test", genres={"test", "test2"})
add = comicapi.genericmetadata.GenericMetadata(
series="test2", issue="2", title="test2", genres={"test3", "test4"}, issue_count=5
)
expected = comicapi.genericmetadata.GenericMetadata(
series="test", issue="1", title="test", genres={"test", "test2"}, issue_count=5
)
md.overlay(add, OverlayMode.add_missing)
assert md == expected
def test_metadata_overlay_combine():
md = comicapi.genericmetadata.GenericMetadata(
series="test",
issue="1",
title="test",
genres={"test", "test2"},
story_arcs=["arc1"],
characters={"Bob", "fred"},
web_links=[parse_url("https://my.comics.here.com")],
)
combine_new = comicapi.genericmetadata.GenericMetadata(
series="test2",
title="test2",
genres={"test2", "test3", "test4"},
story_arcs=["arc1", "arc2"],
characters={"bob", "fred"},
)
expected = comicapi.genericmetadata.GenericMetadata(
series="test2",
issue="1",
title="test2",
genres={"test", "test2", "test3", "test4"},
story_arcs=["arc1", "arc2"],
characters={"bob", "fred"},
web_links=[parse_url("https://my.comics.here.com")],
)
md.overlay(combine_new, OverlayMode.combine)
assert md == expected
def test_assign_dedupe_set():
md_cur = comicapi.genericmetadata.GenericMetadata(characters={"Macintosh", "Søren Kierkegaard", "Barry"})
md_new = comicapi.genericmetadata.GenericMetadata(characters={"MacIntosh", "Soren Kierkegaard"})
# Expect a failure to normalise with NFKD and 'ø'
expected = comicapi.genericmetadata.GenericMetadata(
characters={"MacIntosh", "Soren Kierkegaard", "Søren Kierkegaard", "Barry"}
)
md_cur.overlay(md_new, OverlayMode.combine)
assert md_cur == expected
def test_assign_dedupe_list():
md_cur = comicapi.genericmetadata.GenericMetadata(story_arcs=["arc 1", "arc2", "arc 3"])
md_new = comicapi.genericmetadata.GenericMetadata(story_arcs=["Arc 1", "Arc2"])
expected = comicapi.genericmetadata.GenericMetadata(story_arcs=["Arc 1", "Arc2", "arc 3"])
md_cur.overlay(md_new, OverlayMode.combine)
assert md_cur == expected
def test_assign_credits_overlay():
md = comicapi.genericmetadata.GenericMetadata()
md.add_credit(person="test", role="writer", primary=False)
md.add_credit(person="test", role="artist", primary=True)
md_new = comicapi.genericmetadata.GenericMetadata()
md_new.add_credit(person="", role="writer")
md_new.add_credit(person="test2", role="inker")
expected = comicapi.genericmetadata.GenericMetadata()
expected.add_credit(person="test", role="writer", primary=False)
expected.add_credit(person="test", role="artist", primary=True)
expected.add_credit(person="test2", role="inker")
assert md.assign_credits_overlay(md.credits, md_new.credits) == expected.credits
def test_assign_credits_add_missing():
md = comicapi.genericmetadata.GenericMetadata()
md.add_credit(person="test", role="writer", primary=False)
md.add_credit(person="test", role="artist", primary=True)
md_new = comicapi.genericmetadata.GenericMetadata()
md_new.add_credit(person="Bob", role="writer")
md_new.add_credit(person="test", role="artist", primary=True)
expected = comicapi.genericmetadata.GenericMetadata()
expected.add_credit(person="Bob", role="writer")
expected.add_credit(person="test", role="artist", primary=True)
expected.add_credit(person="test", role="writer", primary=False)
assert md.assign_credits_add_missing(md.credits, md_new.credits) == expected.credits
def test_add_credit():
md = comicapi.genericmetadata.GenericMetadata()