2015-02-21 18:30:32 -08:00
|
|
|
|
"""Some generic utilities"""
|
2024-01-29 09:14:25 -08:00
|
|
|
|
|
2023-02-16 17:23:13 -08:00
|
|
|
|
# Copyright 2012-2014 ComicTagger Authors
|
2022-06-02 18:32:16 -07:00
|
|
|
|
#
|
2015-02-21 18:30:32 -08:00
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
|
# You may obtain a copy of the License at
|
2022-06-02 18:32:16 -07:00
|
|
|
|
#
|
2015-02-21 18:30:32 -08:00
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
2022-06-02 18:32:16 -07:00
|
|
|
|
#
|
2015-02-21 18:30:32 -08:00
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
|
# limitations under the License.
|
2022-06-02 18:32:16 -07:00
|
|
|
|
from __future__ import annotations
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
import json
|
2022-04-04 18:59:26 -07:00
|
|
|
|
import logging
|
2015-02-16 04:27:21 -08:00
|
|
|
|
import os
|
2022-05-19 13:28:18 -07:00
|
|
|
|
import pathlib
|
2023-04-22 22:00:26 -07:00
|
|
|
|
import platform
|
2024-03-03 21:47:31 -08:00
|
|
|
|
import sys
|
2021-09-26 17:06:30 -07:00
|
|
|
|
import unicodedata
|
2023-04-22 22:00:26 -07:00
|
|
|
|
from collections.abc import Iterable, Mapping
|
2024-03-03 21:47:31 -08:00
|
|
|
|
from enum import Enum, auto
|
2022-06-06 16:58:27 -07:00
|
|
|
|
from shutil import which # noqa: F401
|
2025-01-10 16:25:10 -08:00
|
|
|
|
from typing import Any, Callable, TypeVar, cast
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
2024-03-03 21:47:31 -08:00
|
|
|
|
from comicfn2dict import comicfn2dict
|
|
|
|
|
|
2022-10-25 21:48:01 -07:00
|
|
|
|
import comicapi.data
|
2023-09-06 01:50:05 -07:00
|
|
|
|
from comicapi import filenamelexer, filenameparser
|
2024-06-20 16:47:10 -07:00
|
|
|
|
from comicapi._url import LocationParseError as LocationParseError # noqa: F401
|
|
|
|
|
from comicapi._url import Url as Url
|
|
|
|
|
from comicapi._url import parse_url as parse_url
|
2024-02-17 15:08:36 -08:00
|
|
|
|
|
2023-04-22 22:00:26 -07:00
|
|
|
|
try:
|
|
|
|
|
import icu
|
|
|
|
|
|
|
|
|
|
del icu
|
|
|
|
|
icu_available = True
|
|
|
|
|
except ImportError:
|
|
|
|
|
icu_available = False
|
|
|
|
|
|
2024-03-03 21:47:31 -08:00
|
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
|
2024-07-19 15:52:30 -07:00
|
|
|
|
@classmethod
|
|
|
|
|
def _missing_(cls, value: Any) -> str | None:
|
|
|
|
|
if not isinstance(value, str):
|
|
|
|
|
return None
|
|
|
|
|
if not hasattr(cls, "_lower_members"):
|
|
|
|
|
cls._lower_members = {x.casefold(): x for x in cls} # type: ignore[attr-defined]
|
|
|
|
|
return cls._lower_members.get(value.casefold(), None) # type: ignore[attr-defined]
|
|
|
|
|
|
2024-08-18 19:16:55 -07:00
|
|
|
|
def __str__(self) -> str:
|
2024-07-19 15:52:30 -07:00
|
|
|
|
return self.value
|
|
|
|
|
|
2024-03-03 21:47:31 -08:00
|
|
|
|
else:
|
2024-07-19 15:52:30 -07:00
|
|
|
|
from enum import StrEnum as s
|
|
|
|
|
|
|
|
|
|
class StrEnum(s):
|
|
|
|
|
@classmethod
|
|
|
|
|
def _missing_(cls, value: Any) -> str | None:
|
|
|
|
|
if not isinstance(value, str):
|
|
|
|
|
return None
|
|
|
|
|
if not hasattr(cls, "_lower_members"):
|
|
|
|
|
cls._lower_members = {x.casefold(): x for x in cls} # type: ignore[attr-defined]
|
|
|
|
|
return cls._lower_members.get(value.casefold(), None) # type: ignore[attr-defined]
|
2024-03-03 21:47:31 -08:00
|
|
|
|
|
|
|
|
|
|
2022-04-04 18:59:26 -07:00
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
2025-01-10 16:25:10 -08:00
|
|
|
|
class DefaultDict(dict):
|
|
|
|
|
def __init__(self, *args, default: Callable[[Any], Any] | None = None) -> None:
|
|
|
|
|
super().__init__(*args)
|
|
|
|
|
self.default = default
|
|
|
|
|
|
|
|
|
|
def __missing__(self, key: Any) -> Any:
|
|
|
|
|
if self.default is None:
|
|
|
|
|
return key
|
|
|
|
|
return self.default(key)
|
|
|
|
|
|
|
|
|
|
|
2024-03-03 21:47:31 -08:00
|
|
|
|
class Parser(StrEnum):
|
|
|
|
|
ORIGINAL = auto()
|
|
|
|
|
COMPLICATED = auto()
|
|
|
|
|
COMICFN2DICT = auto()
|
|
|
|
|
|
|
|
|
|
|
2023-11-23 15:58:00 -08:00
|
|
|
|
def _custom_key(tup: Any) -> Any:
|
2023-06-22 20:11:40 -07:00
|
|
|
|
import natsort
|
|
|
|
|
|
2023-04-22 22:00:26 -07:00
|
|
|
|
lst = []
|
|
|
|
|
for x in natsort.os_sort_keygen()(tup):
|
|
|
|
|
ret = x
|
|
|
|
|
if len(x) > 1 and isinstance(x[1], int) and isinstance(x[0], str) and x[0] == "":
|
|
|
|
|
ret = ("a", *x[1:])
|
|
|
|
|
|
|
|
|
|
lst.append(ret)
|
|
|
|
|
return tuple(lst)
|
|
|
|
|
|
|
|
|
|
|
2023-11-23 15:58:00 -08:00
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def os_sorted(lst: Iterable[T]) -> Iterable[T]:
|
2023-06-22 20:11:40 -07:00
|
|
|
|
import natsort
|
|
|
|
|
|
2023-04-22 22:00:26 -07:00
|
|
|
|
key = _custom_key
|
|
|
|
|
if icu_available or platform.system() == "Windows":
|
|
|
|
|
key = natsort.os_sort_keygen()
|
|
|
|
|
return sorted(lst, key=key)
|
2015-02-21 18:30:32 -08:00
|
|
|
|
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
2023-09-06 01:50:05 -07:00
|
|
|
|
def parse_filename(
|
|
|
|
|
filename: str,
|
2024-03-03 21:47:31 -08:00
|
|
|
|
parser: Parser = Parser.ORIGINAL,
|
2023-09-06 01:50:05 -07:00
|
|
|
|
remove_c2c: bool = False,
|
|
|
|
|
remove_fcbd: bool = False,
|
|
|
|
|
remove_publisher: bool = False,
|
|
|
|
|
split_words: bool = False,
|
|
|
|
|
allow_issue_start_with_letter: bool = False,
|
|
|
|
|
protofolius_issue_number_scheme: bool = False,
|
|
|
|
|
) -> filenameparser.FilenameInfo:
|
2024-03-03 21:47:31 -08:00
|
|
|
|
fni = filenameparser.FilenameInfo(
|
|
|
|
|
alternate="",
|
|
|
|
|
annual=False,
|
|
|
|
|
archive="",
|
|
|
|
|
c2c=False,
|
|
|
|
|
fcbd=False,
|
|
|
|
|
format="",
|
|
|
|
|
issue="",
|
|
|
|
|
issue_count="",
|
|
|
|
|
publisher="",
|
|
|
|
|
remainder="",
|
|
|
|
|
series="",
|
|
|
|
|
title="",
|
|
|
|
|
volume="",
|
|
|
|
|
volume_count="",
|
|
|
|
|
year="",
|
|
|
|
|
)
|
2024-06-21 20:07:07 -07:00
|
|
|
|
if not filename:
|
|
|
|
|
return fni
|
|
|
|
|
if split_words:
|
|
|
|
|
import wordninja
|
|
|
|
|
|
|
|
|
|
filename, ext = os.path.splitext(filename)
|
|
|
|
|
filename = " ".join(wordninja.split(filename)) + ext
|
2024-03-03 21:47:31 -08:00
|
|
|
|
|
|
|
|
|
if parser == Parser.COMPLICATED:
|
2023-09-06 01:50:05 -07:00
|
|
|
|
lex = filenamelexer.Lex(filename, allow_issue_start_with_letter)
|
|
|
|
|
p = filenameparser.Parse(
|
|
|
|
|
lex.items,
|
|
|
|
|
remove_c2c=remove_c2c,
|
|
|
|
|
remove_fcbd=remove_fcbd,
|
|
|
|
|
remove_publisher=remove_publisher,
|
|
|
|
|
protofolius_issue_number_scheme=protofolius_issue_number_scheme,
|
|
|
|
|
)
|
2024-06-29 18:43:20 -07:00
|
|
|
|
if p.error:
|
|
|
|
|
logger.info("Issue parsing filename: '%s': %s ", filename, p.error.val)
|
2024-03-03 21:47:31 -08:00
|
|
|
|
fni = p.filename_info
|
|
|
|
|
elif parser == Parser.COMICFN2DICT:
|
|
|
|
|
fn2d = comicfn2dict(filename)
|
|
|
|
|
fni = filenameparser.FilenameInfo(
|
|
|
|
|
alternate="",
|
|
|
|
|
annual=False,
|
|
|
|
|
archive=fn2d.get("ext", ""),
|
|
|
|
|
c2c=False,
|
|
|
|
|
fcbd=False,
|
|
|
|
|
issue=fn2d.get("issue", ""),
|
|
|
|
|
issue_count=fn2d.get("issue_count", ""),
|
|
|
|
|
publisher=fn2d.get("publisher", ""),
|
|
|
|
|
remainder=fn2d.get("scan_info", ""),
|
|
|
|
|
series=fn2d.get("series", ""),
|
|
|
|
|
title=fn2d.get("title", ""),
|
|
|
|
|
volume=fn2d.get("volume", ""),
|
|
|
|
|
volume_count=fn2d.get("volume_count", ""),
|
|
|
|
|
year=fn2d.get("year", ""),
|
|
|
|
|
format=fn2d.get("original_format", ""),
|
|
|
|
|
)
|
2023-09-06 01:50:05 -07:00
|
|
|
|
else:
|
|
|
|
|
fnp = filenameparser.FileNameParser()
|
|
|
|
|
fnp.parse_filename(filename)
|
2023-10-23 21:08:55 -07:00
|
|
|
|
fni = filenameparser.FilenameInfo(
|
|
|
|
|
alternate="",
|
|
|
|
|
annual=False,
|
|
|
|
|
archive="",
|
|
|
|
|
c2c=False,
|
|
|
|
|
fcbd=False,
|
|
|
|
|
issue=fnp.issue,
|
|
|
|
|
issue_count=fnp.issue_count,
|
|
|
|
|
publisher="",
|
|
|
|
|
remainder=fnp.remainder,
|
|
|
|
|
series=fnp.series,
|
|
|
|
|
title="",
|
|
|
|
|
volume=fnp.volume,
|
|
|
|
|
volume_count="",
|
|
|
|
|
year=fnp.year,
|
|
|
|
|
format="",
|
|
|
|
|
)
|
2024-03-03 21:47:31 -08:00
|
|
|
|
return fni
|
2023-09-06 01:50:05 -07:00
|
|
|
|
|
|
|
|
|
|
2024-02-04 12:33:10 -08:00
|
|
|
|
def norm_fold(string: str) -> str:
|
|
|
|
|
"""Normalise and casefold string"""
|
|
|
|
|
return unicodedata.normalize("NFKD", string).casefold()
|
|
|
|
|
|
|
|
|
|
|
2022-11-04 15:39:40 -07:00
|
|
|
|
def combine_notes(existing_notes: str | None, new_notes: str | None, split: str) -> str:
|
2022-11-24 01:24:15 -08:00
|
|
|
|
split_notes, split_str, untouched_notes = (existing_notes or "").rpartition(split)
|
|
|
|
|
if split_notes or split_str:
|
2022-11-04 15:39:40 -07:00
|
|
|
|
return (split_notes + (new_notes or "")).strip()
|
|
|
|
|
else:
|
|
|
|
|
return (untouched_notes + "\n" + (new_notes or "")).strip()
|
|
|
|
|
|
|
|
|
|
|
2023-04-25 00:55:23 -07:00
|
|
|
|
def parse_date_str(date_str: str | None) -> tuple[int | None, int | None, int | None]:
|
2022-07-25 11:22:44 -07:00
|
|
|
|
day = None
|
|
|
|
|
month = None
|
|
|
|
|
year = None
|
|
|
|
|
if date_str:
|
|
|
|
|
parts = date_str.split("-")
|
2023-04-25 00:55:23 -07:00
|
|
|
|
year = xlate_int(parts[0])
|
2022-07-25 11:22:44 -07:00
|
|
|
|
if len(parts) > 1:
|
2023-04-25 00:55:23 -07:00
|
|
|
|
month = xlate_int(parts[1])
|
2022-07-25 11:22:44 -07:00
|
|
|
|
if len(parts) > 2:
|
2023-04-25 00:55:23 -07:00
|
|
|
|
day = xlate_int(parts[2])
|
2022-07-25 11:22:44 -07:00
|
|
|
|
return day, month, year
|
|
|
|
|
|
|
|
|
|
|
2023-12-17 15:51:43 -08:00
|
|
|
|
def shorten_path(path: pathlib.Path, path2: pathlib.Path | None = None) -> tuple[pathlib.Path, pathlib.Path]:
|
|
|
|
|
if path2:
|
|
|
|
|
path2 = path2.absolute()
|
|
|
|
|
|
|
|
|
|
path = path.absolute()
|
|
|
|
|
shortened_path: pathlib.Path = path
|
|
|
|
|
relative_path = pathlib.Path(path.anchor)
|
|
|
|
|
|
|
|
|
|
if path.is_relative_to(path.home()):
|
|
|
|
|
relative_path = path.home()
|
|
|
|
|
shortened_path = path.relative_to(path.home())
|
|
|
|
|
if path.is_relative_to(path.cwd()):
|
|
|
|
|
relative_path = path.cwd()
|
|
|
|
|
shortened_path = path.relative_to(path.cwd())
|
|
|
|
|
|
|
|
|
|
if path2 and shortened_path.is_relative_to(path2.parent):
|
|
|
|
|
relative_path = path2
|
|
|
|
|
shortened_path = shortened_path.relative_to(path2)
|
|
|
|
|
|
|
|
|
|
return relative_path, shortened_path
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def path_to_short_str(original_path: pathlib.Path, renamed_path: pathlib.Path | None = None) -> str:
|
|
|
|
|
rel, _original_path = shorten_path(original_path)
|
|
|
|
|
path_str = str(_original_path)
|
|
|
|
|
if rel.samefile(rel.cwd()):
|
|
|
|
|
path_str = f"./{_original_path}"
|
|
|
|
|
elif rel.samefile(rel.home()):
|
|
|
|
|
path_str = f"~/{_original_path}"
|
|
|
|
|
|
|
|
|
|
if renamed_path:
|
|
|
|
|
rel, path = shorten_path(renamed_path, original_path.parent)
|
|
|
|
|
rename_str = f" -> {path}"
|
|
|
|
|
if rel.samefile(rel.cwd()):
|
|
|
|
|
rename_str = f" -> ./{_original_path}"
|
|
|
|
|
elif rel.samefile(rel.home()):
|
|
|
|
|
rename_str = f" -> ~/{_original_path}"
|
|
|
|
|
path_str += rename_str
|
|
|
|
|
|
|
|
|
|
return path_str
|
|
|
|
|
|
|
|
|
|
|
2023-12-20 21:24:12 -08:00
|
|
|
|
def get_page_name_list(files: list[str]) -> list[str]:
|
|
|
|
|
# get the list file names in the archive, and sort
|
|
|
|
|
files = cast(list[str], os_sorted(files))
|
|
|
|
|
|
|
|
|
|
# make a sub-list of image files
|
|
|
|
|
page_list = []
|
|
|
|
|
for name in files:
|
|
|
|
|
if (
|
2023-12-21 17:52:58 -08:00
|
|
|
|
os.path.splitext(name)[1].casefold() in [".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif"]
|
2023-12-20 21:24:12 -08:00
|
|
|
|
and os.path.basename(name)[0] != "."
|
|
|
|
|
):
|
|
|
|
|
page_list.append(name)
|
|
|
|
|
return page_list
|
|
|
|
|
|
|
|
|
|
|
2022-06-02 18:32:16 -07:00
|
|
|
|
def get_recursive_filelist(pathlist: list[str]) -> list[str]:
|
2015-02-21 18:30:32 -08:00
|
|
|
|
"""Get a recursive list of of all files under all path items in the list"""
|
|
|
|
|
|
2022-07-01 16:22:01 -07:00
|
|
|
|
filelist: list[str] = []
|
2015-02-21 18:30:32 -08:00
|
|
|
|
for p in pathlist:
|
|
|
|
|
if os.path.isdir(p):
|
2023-04-13 19:58:30 -07:00
|
|
|
|
for root, _, files in os.walk(p):
|
|
|
|
|
for f in files:
|
|
|
|
|
filelist.append(os.path.join(root, f))
|
2023-07-29 00:07:25 -07:00
|
|
|
|
elif os.path.exists(p):
|
2023-04-13 19:58:30 -07:00
|
|
|
|
filelist.append(p)
|
2015-02-21 18:30:32 -08:00
|
|
|
|
|
|
|
|
|
return filelist
|
|
|
|
|
|
|
|
|
|
|
2022-05-17 13:57:04 -07:00
|
|
|
|
def add_to_path(dirname: str) -> None:
|
2024-01-20 10:34:40 -08:00
|
|
|
|
if dirname:
|
2022-08-08 18:03:29 -07:00
|
|
|
|
dirname = os.path.abspath(dirname)
|
Convert ComicIssue into GenericMetadata
I could not find a good reason for ComicIssue to exist other than that
it had more attributes than GenericMetadata, so it has been replaced.
New attributes for GenericMetadata:
series_id: a string uniquely identifying the series to tag_origin
series_aliases: alternate series names that are not the canonical name
title_aliases: alternate issue titles that are not the canonical name
alternate_images: a list of urls to alternate cover images
Updated attributes for GenericMetadata:
genre -> genres: str -> list[str]
comments -> description: str -> str
story_arc -> story_arcs: str -> list[str]
series_group -> series_groups: str -> list[str]
character -> characters: str -> list[str]
team -> teams: str -> list[str]
location -> locations: str -> list[str]
tag_origin -> tag_origin: str -> TagOrigin (tuple[str, str])
ComicSeries has been relocated to the ComicAPI package, currently has no
usage within ComicAPI.
CreditMetadata has been renamed to Credit and has replaced Credit from
ComicTalker.
fetch_series has been added to ComicTalker, this is currently only used
in the GUI when a series is selected and does not already contain the
needed fields, this function should always be cached.
A new split function has been added to ComicAPI, all uses of split on
single characters have been updated to use this
cleanup_html and the corresponding setting are now only used in
ComicTagger proper, for display we want any html directly from the
upstream. When applying the metadata we then strip the description of
any html.
A new conversion has been added to the MetadataFormatter:
j: joins any lists into a string with ', '. Note this is a valid
operation on strings as well, it will add ', ' in between every
character.
parse_settings now assigns the given ComicTaggerPaths object to the
result ensuring that the correct path is always used.
2023-08-02 09:00:04 -07:00
|
|
|
|
paths = [os.path.normpath(x) for x in split(os.environ["PATH"], os.pathsep)]
|
2015-02-21 18:30:32 -08:00
|
|
|
|
|
2022-08-08 18:03:29 -07:00
|
|
|
|
if dirname not in paths:
|
|
|
|
|
paths.insert(0, dirname)
|
|
|
|
|
os.environ["PATH"] = os.pathsep.join(paths)
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
|
|
|
|
|
2024-02-18 01:40:49 -08:00
|
|
|
|
def remove_from_path(dirname: str) -> None:
|
|
|
|
|
if dirname:
|
|
|
|
|
dirname = os.path.abspath(dirname)
|
|
|
|
|
paths = [os.path.normpath(x) for x in split(os.environ["PATH"], os.pathsep) if dirname != os.path.normpath(x)]
|
|
|
|
|
|
|
|
|
|
os.environ["PATH"] = os.pathsep.join(paths)
|
|
|
|
|
|
|
|
|
|
|
2023-04-25 00:55:23 -07:00
|
|
|
|
def xlate_int(data: Any) -> int | None:
|
|
|
|
|
data = xlate_float(data)
|
|
|
|
|
if data is None:
|
|
|
|
|
return None
|
|
|
|
|
return int(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def xlate_float(data: Any) -> float | None:
|
2023-06-15 20:25:19 -07:00
|
|
|
|
if isinstance(data, str):
|
|
|
|
|
data = data.strip()
|
2023-04-25 00:55:23 -07:00
|
|
|
|
if data is None or data == "":
|
|
|
|
|
return None
|
|
|
|
|
i: str | int | float
|
|
|
|
|
if isinstance(data, (int, float)):
|
|
|
|
|
i = data
|
|
|
|
|
else:
|
2025-01-10 16:25:10 -08:00
|
|
|
|
i = str(data).translate(
|
|
|
|
|
DefaultDict(zip((ord(c) for c in "1234567890."), "1234567890."), default=lambda x: None)
|
|
|
|
|
)
|
2023-04-25 00:55:23 -07:00
|
|
|
|
if i == "":
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
return float(i)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def xlate(data: Any) -> str | None:
|
2023-06-15 20:25:19 -07:00
|
|
|
|
if data is None or isinstance(data, str) and data.strip() == "":
|
2021-08-07 21:54:29 -07:00
|
|
|
|
return None
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
Convert ComicIssue into GenericMetadata
I could not find a good reason for ComicIssue to exist other than that
it had more attributes than GenericMetadata, so it has been replaced.
New attributes for GenericMetadata:
series_id: a string uniquely identifying the series to tag_origin
series_aliases: alternate series names that are not the canonical name
title_aliases: alternate issue titles that are not the canonical name
alternate_images: a list of urls to alternate cover images
Updated attributes for GenericMetadata:
genre -> genres: str -> list[str]
comments -> description: str -> str
story_arc -> story_arcs: str -> list[str]
series_group -> series_groups: str -> list[str]
character -> characters: str -> list[str]
team -> teams: str -> list[str]
location -> locations: str -> list[str]
tag_origin -> tag_origin: str -> TagOrigin (tuple[str, str])
ComicSeries has been relocated to the ComicAPI package, currently has no
usage within ComicAPI.
CreditMetadata has been renamed to Credit and has replaced Credit from
ComicTalker.
fetch_series has been added to ComicTalker, this is currently only used
in the GUI when a series is selected and does not already contain the
needed fields, this function should always be cached.
A new split function has been added to ComicAPI, all uses of split on
single characters have been updated to use this
cleanup_html and the corresponding setting are now only used in
ComicTagger proper, for display we want any html directly from the
upstream. When applying the metadata we then strip the description of
any html.
A new conversion has been added to the MetadataFormatter:
j: joins any lists into a string with ', '. Note this is a valid
operation on strings as well, it will add ', ' in between every
character.
parse_settings now assigns the given ComicTaggerPaths object to the
result ensuring that the correct path is always used.
2023-08-02 09:00:04 -07:00
|
|
|
|
return str(data).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def split(s: str | None, c: str) -> list[str]:
|
|
|
|
|
s = xlate(s)
|
|
|
|
|
if s:
|
|
|
|
|
return [x.strip() for x in s.strip().split(c) if x.strip()]
|
|
|
|
|
return []
|
2021-08-07 21:54:29 -07:00
|
|
|
|
|
|
|
|
|
|
2024-02-17 15:08:36 -08:00
|
|
|
|
def split_urls(s: str | None) -> list[Url]:
|
|
|
|
|
if s is None:
|
|
|
|
|
return []
|
|
|
|
|
# Find occurences of ' http'
|
|
|
|
|
if s.count("http") > 1 and s.count(" http") >= 1:
|
|
|
|
|
urls = []
|
|
|
|
|
# Split urls out
|
|
|
|
|
url_strings = split(s, " http")
|
|
|
|
|
# Return the scheme 'http' and parse the url
|
|
|
|
|
for i, url_string in enumerate(url_strings):
|
|
|
|
|
if not url_string.startswith("http"):
|
|
|
|
|
url_string = "http" + url_string
|
|
|
|
|
urls.append(parse_url(url_string))
|
|
|
|
|
return urls
|
|
|
|
|
else:
|
|
|
|
|
return [parse_url(s)]
|
|
|
|
|
|
|
|
|
|
|
2022-05-17 13:57:04 -07:00
|
|
|
|
def remove_articles(text: str) -> str:
|
2022-07-01 16:22:01 -07:00
|
|
|
|
text = text.casefold()
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
articles = [
|
|
|
|
|
"&",
|
|
|
|
|
"a",
|
|
|
|
|
"am",
|
|
|
|
|
"an",
|
|
|
|
|
"and",
|
|
|
|
|
"as",
|
|
|
|
|
"at",
|
|
|
|
|
"be",
|
|
|
|
|
"but",
|
|
|
|
|
"by",
|
|
|
|
|
"for",
|
|
|
|
|
"if",
|
|
|
|
|
"is",
|
|
|
|
|
"issue",
|
|
|
|
|
"it",
|
|
|
|
|
"it's",
|
|
|
|
|
"its",
|
|
|
|
|
"itself",
|
|
|
|
|
"of",
|
|
|
|
|
"or",
|
|
|
|
|
"so",
|
|
|
|
|
"the",
|
|
|
|
|
"the",
|
|
|
|
|
"with",
|
|
|
|
|
]
|
|
|
|
|
new_text = ""
|
2022-08-12 07:10:36 -07:00
|
|
|
|
for word in text.split():
|
2015-02-21 18:30:32 -08:00
|
|
|
|
if word not in articles:
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
new_text += word + " "
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
new_text = new_text[:-1]
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
return new_text
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
|
|
|
|
|
2022-06-07 11:49:56 -07:00
|
|
|
|
def sanitize_title(text: str, basic: bool = False) -> str:
|
2021-09-26 17:06:30 -07:00
|
|
|
|
# normalize unicode and convert to ascii. Does not work for everything eg ½ to 1⁄2 not 1/2
|
2022-07-09 23:26:30 -07:00
|
|
|
|
text = unicodedata.normalize("NFKD", text).casefold()
|
2022-08-12 07:10:36 -07:00
|
|
|
|
# comicvine keeps apostrophes a part of the word
|
|
|
|
|
text = text.replace("'", "")
|
|
|
|
|
text = text.replace('"', "")
|
|
|
|
|
if not basic:
|
2022-07-09 23:26:30 -07:00
|
|
|
|
# comicvine ignores punctuation and accents
|
|
|
|
|
# remove all characters that are not a letter, separator (space) or number
|
|
|
|
|
# replace any "dash punctuation" with a space
|
|
|
|
|
# makes sure that batman-superman and self-proclaimed stay separate words
|
|
|
|
|
text = "".join(
|
2022-08-12 07:10:36 -07:00
|
|
|
|
c if unicodedata.category(c)[0] not in "P" else " " for c in text if unicodedata.category(c)[0] in "LZNP"
|
2022-07-09 23:26:30 -07:00
|
|
|
|
)
|
2022-06-07 11:49:56 -07:00
|
|
|
|
# remove extra space and articles and all lower case
|
2022-07-09 23:26:30 -07:00
|
|
|
|
text = remove_articles(text).strip()
|
2021-09-26 17:06:30 -07:00
|
|
|
|
|
|
|
|
|
return text
|
|
|
|
|
|
|
|
|
|
|
2022-08-08 18:03:29 -07:00
|
|
|
|
def titles_match(search_title: str, record_title: str, threshold: int = 90) -> bool:
|
2023-06-22 20:11:40 -07:00
|
|
|
|
import rapidfuzz.fuzz
|
|
|
|
|
|
2022-07-09 23:26:30 -07:00
|
|
|
|
sanitized_search = sanitize_title(search_title)
|
|
|
|
|
sanitized_record = sanitize_title(record_title)
|
2023-02-09 19:33:10 -08:00
|
|
|
|
ratio = int(rapidfuzz.fuzz.ratio(sanitized_search, sanitized_record))
|
2022-07-08 12:33:00 -07:00
|
|
|
|
logger.debug(
|
|
|
|
|
"search title: %s ; record title: %s ; ratio: %d ; match threshold: %d",
|
|
|
|
|
search_title,
|
|
|
|
|
record_title,
|
|
|
|
|
ratio,
|
|
|
|
|
threshold,
|
|
|
|
|
)
|
|
|
|
|
return ratio >= threshold
|
2022-07-09 23:26:30 -07:00
|
|
|
|
|
|
|
|
|
|
2022-07-09 22:27:45 -07:00
|
|
|
|
def unique_file(file_name: pathlib.Path) -> pathlib.Path:
|
2022-08-19 20:20:37 -07:00
|
|
|
|
name = file_name.stem
|
2015-02-21 18:30:32 -08:00
|
|
|
|
counter = 1
|
|
|
|
|
while True:
|
2022-07-09 22:27:45 -07:00
|
|
|
|
if not file_name.exists():
|
2015-02-21 18:30:32 -08:00
|
|
|
|
return file_name
|
2022-08-19 20:20:37 -07:00
|
|
|
|
file_name = file_name.with_stem(name + " (" + str(counter) + ")")
|
2015-02-21 18:30:32 -08:00
|
|
|
|
counter += 1
|
2015-02-16 04:27:21 -08:00
|
|
|
|
|
|
|
|
|
|
2023-12-17 15:51:43 -08:00
|
|
|
|
def parse_version(s: str) -> tuple[int, int, int]:
|
|
|
|
|
str_parts = s.split(".")[:3]
|
|
|
|
|
parts = [int(x) if x.isdigit() else 0 for x in str_parts]
|
|
|
|
|
parts.extend([0] * (3 - len(parts))) # Ensure exactly three elements in the resulting list
|
|
|
|
|
|
|
|
|
|
return (parts[0], parts[1], parts[2])
|
|
|
|
|
|
|
|
|
|
|
2025-01-10 16:25:10 -08:00
|
|
|
|
_languages: dict[str | None, str | None] = DefaultDict(default=lambda x: None)
|
2023-06-22 20:11:40 -07:00
|
|
|
|
|
2025-01-10 16:25:10 -08:00
|
|
|
|
_countries: dict[str | None, str | None] = DefaultDict(default=lambda x: None)
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
|
|
|
|
|
2023-06-22 20:11:40 -07:00
|
|
|
|
def countries() -> dict[str | None, str | None]:
|
|
|
|
|
if not _countries:
|
2023-11-23 14:21:21 -08:00
|
|
|
|
import isocodes
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
2023-11-23 14:21:21 -08:00
|
|
|
|
for alpha_2, c in isocodes.countries.by_alpha_2:
|
|
|
|
|
_countries[alpha_2] = c["name"]
|
2024-12-16 19:13:56 -08:00
|
|
|
|
return _countries.copy()
|
2023-06-22 20:11:40 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def languages() -> dict[str | None, str | None]:
|
|
|
|
|
if not _languages:
|
2023-11-23 14:21:21 -08:00
|
|
|
|
import isocodes
|
2023-06-22 20:11:40 -07:00
|
|
|
|
|
2023-11-23 14:21:21 -08:00
|
|
|
|
for alpha_2, lng in isocodes.extendend_languages._sorted_by_index(index="alpha_2"):
|
|
|
|
|
_languages[alpha_2] = lng["name"]
|
2024-12-16 19:13:56 -08:00
|
|
|
|
return _languages.copy()
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
|
|
|
|
|
2022-06-02 18:32:16 -07:00
|
|
|
|
def get_language_from_iso(iso: str | None) -> str | None:
|
2025-01-10 16:25:10 -08:00
|
|
|
|
if not _languages:
|
|
|
|
|
return languages()[iso]
|
2024-12-16 19:13:56 -08:00
|
|
|
|
return _languages[iso]
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
|
|
|
|
|
2022-10-02 19:33:12 -07:00
|
|
|
|
def get_language_iso(string: str | None) -> str | None:
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
if string is None:
|
2015-02-21 18:30:32 -08:00
|
|
|
|
return None
|
2023-11-23 14:21:21 -08:00
|
|
|
|
import isocodes
|
2023-06-22 20:11:40 -07:00
|
|
|
|
|
2023-05-24 17:32:52 -07:00
|
|
|
|
# Return current string if all else fails
|
2022-10-02 19:33:12 -07:00
|
|
|
|
lang = string.casefold()
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
|
2023-11-23 14:21:21 -08:00
|
|
|
|
found = None
|
2025-01-10 16:25:10 -08:00
|
|
|
|
|
2023-11-23 14:21:21 -08:00
|
|
|
|
for lng in isocodes.extendend_languages.items:
|
|
|
|
|
for x in ("alpha_2", "alpha_3", "bibliographic", "common_name", "name"):
|
|
|
|
|
if x in lng and lng[x].casefold() == lang:
|
|
|
|
|
found = lng
|
2025-01-10 16:25:10 -08:00
|
|
|
|
# break
|
2023-11-23 14:21:21 -08:00
|
|
|
|
if found:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if found:
|
|
|
|
|
return found.get("alpha_2", None)
|
Code cleanup
Remove no longer used google scripts
Remove convenience files from comicataggerlib and import comicapi directly
Add type-hints to facilitate auto-complete tools
Make PyQt5 code more compatible with PyQt6
Implement automatic tooling
isort and black for code formatting
Line length has been set to 120
flake8 for code standards with exceptions:
E203 - Whitespace before ':' - format compatiblity with black
E501 - Line too long - flake8 line limit cannot be set
E722 - Do not use bare except - fixing bare except statements is a
lot of overhead and there are already
many in the codebase
These changes, along with some manual fixes creates much more readable code.
See examples below:
diff --git a/comicapi/comet.py b/comicapi/comet.py
index d1741c5..52dc195 100644
--- a/comicapi/comet.py
+++ b/comicapi/comet.py
@@ -166,7 +166,2 @@ class CoMet:
- if credit['role'].lower() in set(self.editor_synonyms):
- ET.SubElement(
- root,
- 'editor').text = "{0}".format(
- credit['person'])
@@ -174,2 +169,4 @@ class CoMet:
self.indent(root)
+ if credit["role"].lower() in set(self.editor_synonyms):
+ ET.SubElement(root, "editor").text = str(credit["person"])
diff --git a/comictaggerlib/autotagmatchwindow.py b/comictaggerlib/autotagmatchwindow.py
index 4338176..9219f01 100644
--- a/comictaggerlib/autotagmatchwindow.py
+++ b/comictaggerlib/autotagmatchwindow.py
@@ -63,4 +63,3 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
self.skipButton, QtWidgets.QDialogButtonBox.ActionRole)
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setText(
- "Accept and Write Tags")
+ self.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Ok).setText("Accept and Write Tags")
diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py
index 688907d..dbd0c2e 100644
--- a/comictaggerlib/cli.py
+++ b/comictaggerlib/cli.py
@@ -293,7 +293,3 @@ def process_file_cli(filename, opts, settings, match_results):
if opts.raw:
- print((
- "{0}".format(
- str(
- ca.readRawCIX(),
- errors='ignore'))))
+ print(ca.read_raw_cix())
else:
2022-04-01 16:50:46 -07:00
|
|
|
|
return lang
|
2022-05-19 13:28:18 -07:00
|
|
|
|
|
|
|
|
|
|
2023-05-22 16:02:56 -07:00
|
|
|
|
def get_country_from_iso(iso: str | None) -> str | None:
|
2025-01-10 16:25:10 -08:00
|
|
|
|
if not _countries:
|
|
|
|
|
return countries()[iso]
|
2024-12-16 19:13:56 -08:00
|
|
|
|
return _countries[iso]
|
2023-05-22 16:02:56 -07:00
|
|
|
|
|
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
def get_publisher(publisher: str) -> tuple[str, str]:
|
2021-08-07 21:50:45 -07:00
|
|
|
|
imprint = ""
|
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
for pub in publishers.values():
|
2021-08-07 21:50:45 -07:00
|
|
|
|
imprint, publisher, ok = pub[publisher]
|
|
|
|
|
if ok:
|
|
|
|
|
break
|
|
|
|
|
|
2023-06-09 16:20:00 -07:00
|
|
|
|
return imprint, publisher
|
2021-08-07 21:50:45 -07:00
|
|
|
|
|
|
|
|
|
|
2022-06-02 18:32:16 -07:00
|
|
|
|
def update_publishers(new_publishers: Mapping[str, Mapping[str, str]]) -> None:
|
2022-05-19 13:28:18 -07:00
|
|
|
|
for publisher in new_publishers:
|
|
|
|
|
if publisher in publishers:
|
|
|
|
|
publishers[publisher].update(new_publishers[publisher])
|
|
|
|
|
else:
|
|
|
|
|
publishers[publisher] = ImprintDict(publisher, new_publishers[publisher])
|
|
|
|
|
|
|
|
|
|
|
2022-12-06 00:20:01 -08:00
|
|
|
|
class ImprintDict(dict): # type: ignore
|
2022-05-19 13:28:18 -07:00
|
|
|
|
"""
|
2021-08-07 21:50:45 -07:00
|
|
|
|
ImprintDict takes a publisher and a dict or mapping of lowercased
|
2022-06-07 20:22:33 -07:00
|
|
|
|
imprint names to the proper imprint name. Retrieving a value from an
|
2021-08-07 21:50:45 -07:00
|
|
|
|
ImprintDict returns a tuple of (imprint, publisher, keyExists).
|
|
|
|
|
if the key does not exist the key is returned as the publisher unchanged
|
2022-05-19 13:28:18 -07:00
|
|
|
|
"""
|
|
|
|
|
|
2022-12-06 00:20:01 -08:00
|
|
|
|
def __init__(self, publisher: str, mapping: tuple | Mapping = (), **kwargs: dict) -> None: # type: ignore
|
2021-08-07 21:50:45 -07:00
|
|
|
|
super().__init__(mapping, **kwargs)
|
|
|
|
|
self.publisher = publisher
|
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
def __missing__(self, key: str) -> None:
|
2021-08-07 21:50:45 -07:00
|
|
|
|
return None
|
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
def __getitem__(self, k: str) -> tuple[str, str, bool]:
|
|
|
|
|
item = super().__getitem__(k.casefold())
|
|
|
|
|
if k.casefold() == self.publisher.casefold():
|
2023-06-09 16:20:00 -07:00
|
|
|
|
return "", self.publisher, True
|
2021-08-07 21:50:45 -07:00
|
|
|
|
if item is None:
|
2023-06-09 16:20:00 -07:00
|
|
|
|
return "", k, False
|
2021-08-07 21:50:45 -07:00
|
|
|
|
else:
|
2023-06-09 16:20:00 -07:00
|
|
|
|
return item, self.publisher, True
|
2021-08-07 21:50:45 -07:00
|
|
|
|
|
2022-06-02 18:32:16 -07:00
|
|
|
|
def copy(self) -> ImprintDict:
|
2022-05-21 00:16:45 -07:00
|
|
|
|
return ImprintDict(self.publisher, super().copy())
|
|
|
|
|
|
2022-05-19 13:28:18 -07:00
|
|
|
|
|
|
|
|
|
publishers: dict[str, ImprintDict] = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_publishers() -> None:
|
2022-05-19 20:13:59 -07:00
|
|
|
|
try:
|
2022-10-25 21:48:01 -07:00
|
|
|
|
update_publishers(json.loads((comicapi.data.data_path / "publishers.json").read_text("utf-8")))
|
2022-05-19 20:13:59 -07:00
|
|
|
|
except Exception:
|
|
|
|
|
logger.exception("Failed to load publishers.json; The are no publishers or imprints loaded")
|