Use regex for stripping
This commit is contained in:
79
__init__.py
79
__init__.py
@@ -2,6 +2,7 @@ from picard import config, log
|
|||||||
from picard.ui.options import OptionsPage, register_options_page
|
from picard.ui.options import OptionsPage, register_options_page
|
||||||
from picard.metadata import register_track_metadata_processor
|
from picard.metadata import register_track_metadata_processor
|
||||||
from PyQt5 import QtWidgets
|
from PyQt5 import QtWidgets
|
||||||
|
import re
|
||||||
|
|
||||||
from .constants import *
|
from .constants import *
|
||||||
|
|
||||||
@@ -57,58 +58,42 @@ def process_track(_, metadata, track, __):
|
|||||||
metadata["~releasecomment"] = stripped_album_disambiguation
|
metadata["~releasecomment"] = stripped_album_disambiguation
|
||||||
|
|
||||||
def strip_keyword_from_disambiguation(disambiguation, keyword):
|
def strip_keyword_from_disambiguation(disambiguation, keyword):
|
||||||
# keyword is the entire disambiguation (e,g. "explicit" becomes "")
|
disambiguation_stripped = disambiguation.strip()
|
||||||
if disambiguation.strip().lower() == keyword.lower():
|
keyword_stripped = keyword.strip()
|
||||||
return ""
|
|
||||||
|
|
||||||
# keyword is at the end with a preceding comma and space (e.g. "original mix, explicit" becomes "original mix")
|
if not disambiguation_stripped or not keyword_stripped:
|
||||||
if disambiguation.strip().lower().endswith(", " + keyword.lower()):
|
return disambiguation
|
||||||
return disambiguation[:-len(keyword)-2].strip()
|
|
||||||
|
|
||||||
# keyword is at the end of the disambiguation preceded by a space (e.g. "original mix explicit" becomes "original mix")
|
keyword_pattern = re.compile(rf"(?<!\\w){re.escape(keyword_stripped)}(?!\\w)", re.IGNORECASE)
|
||||||
if disambiguation.strip().lower().endswith(" " + keyword.lower()):
|
if not keyword_pattern.search(disambiguation_stripped):
|
||||||
return disambiguation[:-len(keyword)].strip()
|
|
||||||
|
|
||||||
# keyword is at the start of the disambiguation (e.g. "explicit, original mix" becomes "original mix")
|
|
||||||
if disambiguation.strip().lower().startswith(keyword.lower() + ","):
|
|
||||||
return disambiguation[len(keyword)+1:].strip()
|
|
||||||
|
|
||||||
# keyword is at the start of the disambiguation followed by a space (e.g. "explicit album version" becomes "album version")
|
|
||||||
if disambiguation.strip().lower().startswith(keyword.lower() + " "):
|
|
||||||
return disambiguation[len(keyword):].strip()
|
|
||||||
|
|
||||||
# keyword is separated by a dash in the beginning of the disambiguation (e.g. "explicit - original mix" becomes "original mix")
|
|
||||||
if disambiguation.strip().lower().startswith(keyword.lower() + " -"):
|
|
||||||
return disambiguation[len(keyword)+2:].strip()
|
|
||||||
|
|
||||||
# keyword is at the end of the disambiguation (e.g. "original mix,explicit" becomes "original mix")
|
|
||||||
if disambiguation.strip().lower().endswith("," + keyword.lower()):
|
|
||||||
return disambiguation[:-len(keyword)-1].strip()
|
|
||||||
|
|
||||||
# keyword is separated by a dash in the end of the disambiguation (e.g. "original mix - explicit" becomes "original mix")
|
|
||||||
if disambiguation.strip().lower().endswith("- " + keyword.lower()):
|
|
||||||
return disambiguation[:-len(keyword)-2].strip()
|
|
||||||
|
|
||||||
# keyword is in between brackets in the disambiguation (e.g. "original mix (explicit)" becomes "original mix")
|
|
||||||
if f"({keyword.lower()})" in disambiguation.strip().lower():
|
|
||||||
return disambiguation.replace(f"({keyword})", "").strip()
|
|
||||||
|
|
||||||
# keyword is a standalone word in the disambiguation (e.g. "original explicit mix" becomes "original mix")
|
|
||||||
if f" {keyword.lower()} " in disambiguation.strip().lower():
|
|
||||||
return disambiguation.replace(f" {keyword} ", " ").strip()
|
|
||||||
|
|
||||||
# keyword is in the middle of the disambiguation (e.g. "album version, explicit, remix" becomes "album version, remix")
|
|
||||||
if "," + keyword.lower() + "," in disambiguation.strip().lower():
|
|
||||||
return disambiguation.replace("," + keyword + ",", ",").strip()
|
|
||||||
|
|
||||||
# keyword is separated by a dash in the middle of the disambiguation (e.g. "album version - explicit - remix" becomes "album version - remix")
|
|
||||||
if " -" + keyword.lower() + "- " in disambiguation.strip().lower():
|
|
||||||
return disambiguation.replace(" -" + keyword + "- ", " - ").strip()
|
|
||||||
|
|
||||||
# Return the disambiguation unchanged if the keyword is not found or cannot be stripped
|
|
||||||
log.debug(f"Keyword '{keyword}' not found in disambiguation '{disambiguation}' for stripping")
|
log.debug(f"Keyword '{keyword}' not found in disambiguation '{disambiguation}' for stripping")
|
||||||
return disambiguation
|
return disambiguation
|
||||||
|
|
||||||
|
parts = re.split(r"\s*(,|\||-)\s*", disambiguation_stripped)
|
||||||
|
terms = parts[::2]
|
||||||
|
separators = parts[1::2]
|
||||||
|
|
||||||
|
cleaned_terms = []
|
||||||
|
for term in terms:
|
||||||
|
cleaned_term = keyword_pattern.sub("", term)
|
||||||
|
cleaned_term = re.sub(r"\(\s*\)", "", cleaned_term)
|
||||||
|
cleaned_term = re.sub(r"\s{2,}", " ", cleaned_term).strip()
|
||||||
|
cleaned_terms.append(cleaned_term)
|
||||||
|
|
||||||
|
non_empty_indexes = [i for i, term in enumerate(cleaned_terms) if term]
|
||||||
|
if not non_empty_indexes:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
result = cleaned_terms[non_empty_indexes[0]]
|
||||||
|
for index in non_empty_indexes[1:]:
|
||||||
|
separator = separators[index - 1] if index - 1 < len(separators) else ","
|
||||||
|
if separator == ",":
|
||||||
|
result += f", {cleaned_terms[index]}"
|
||||||
|
else:
|
||||||
|
result += f" {separator} {cleaned_terms[index]}"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
class ECD2ITatOptionsPage(OptionsPage):
|
class ECD2ITatOptionsPage(OptionsPage):
|
||||||
NAME = "ecd2itat"
|
NAME = "ecd2itat"
|
||||||
TITLE = "ecd2iTat"
|
TITLE = "ecd2iTat"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from picard.config import BoolOption, TextOption, Option
|
|||||||
PLUGIN_NAME = "ecd2iTat"
|
PLUGIN_NAME = "ecd2iTat"
|
||||||
PLUGIN_AUTHOR = "cy1der"
|
PLUGIN_AUTHOR = "cy1der"
|
||||||
PLUGIN_DESCRIPTION = "Convert disambiguations containing \"explicit\"/\"clean\" (and others) keywords to proper tags so clients can display the 🅴/🅲 symbol"
|
PLUGIN_DESCRIPTION = "Convert disambiguations containing \"explicit\"/\"clean\" (and others) keywords to proper tags so clients can display the 🅴/🅲 symbol"
|
||||||
PLUGIN_VERSION = "1.0.2"
|
PLUGIN_VERSION = "1.0.3"
|
||||||
PLUGIN_API_VERSIONS = ["2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13"]
|
PLUGIN_API_VERSIONS = ["2.7", "2.8", "2.9", "2.10", "2.11", "2.12", "2.13"]
|
||||||
PLUGIN_LICENSE = "GPL-2.0-or-later"
|
PLUGIN_LICENSE = "GPL-2.0-or-later"
|
||||||
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"
|
PLUGIN_LICENSE_URL = "https://www.gnu.org/licenses/gpl-2.0.html"
|
||||||
|
|||||||
81
tests.py
Normal file
81
tests.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import ast
|
||||||
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from typing import Callable, cast
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def _load_strip_keyword_function():
|
||||||
|
module_path = Path(__file__).resolve().parent / "__init__.py"
|
||||||
|
source = module_path.read_text(encoding="utf-8")
|
||||||
|
tree = ast.parse(source, filename=str(module_path))
|
||||||
|
|
||||||
|
function_node = next(
|
||||||
|
node
|
||||||
|
for node in tree.body
|
||||||
|
if isinstance(node, ast.FunctionDef) and node.name == "strip_keyword_from_disambiguation"
|
||||||
|
)
|
||||||
|
|
||||||
|
isolated_module = ast.Module(
|
||||||
|
body=[
|
||||||
|
ast.Import(names=[ast.alias(name="re")]),
|
||||||
|
function_node,
|
||||||
|
],
|
||||||
|
type_ignores=[],
|
||||||
|
)
|
||||||
|
ast.fix_missing_locations(isolated_module)
|
||||||
|
|
||||||
|
namespace: dict[str, object] = {
|
||||||
|
"log": SimpleNamespace(debug=lambda *args, **kwargs: None),
|
||||||
|
}
|
||||||
|
exec(compile(isolated_module, str(module_path), "exec"), namespace)
|
||||||
|
return cast(Callable[[str, str], str], namespace["strip_keyword_from_disambiguation"])
|
||||||
|
|
||||||
|
|
||||||
|
strip_keyword_from_disambiguation = _load_strip_keyword_function()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"disambiguation, keyword, expected",
|
||||||
|
[
|
||||||
|
("explicit", "explicit", ""),
|
||||||
|
("original mix, explicit", "explicit", "original mix"),
|
||||||
|
("original mix explicit", "explicit", "original mix"),
|
||||||
|
("explicit, original mix", "explicit", "original mix"),
|
||||||
|
("explicit album version", "explicit", "album version"),
|
||||||
|
("explicit - original mix", "explicit", "original mix"),
|
||||||
|
("original mix,explicit", "explicit", "original mix"),
|
||||||
|
("original mix - explicit", "explicit", "original mix"),
|
||||||
|
("original mix (explicit)", "explicit", "original mix"),
|
||||||
|
("original mix ( explicit )", "explicit", "original mix"),
|
||||||
|
("original explicit mix", "explicit", "original mix"),
|
||||||
|
("album version, explicit, remix", "explicit", "album version, remix"),
|
||||||
|
("album version - explicit - remix", "explicit", "album version - remix"),
|
||||||
|
("album version | explicit | remix", "explicit", "album version | remix"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_strip_keyword_supported_patterns(disambiguation, keyword, expected):
|
||||||
|
assert strip_keyword_from_disambiguation(disambiguation, keyword) == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_strip_keyword_is_case_insensitive():
|
||||||
|
result = strip_keyword_from_disambiguation("Original Mix, EXPLICIT", "explicit")
|
||||||
|
assert result == "Original Mix"
|
||||||
|
|
||||||
|
|
||||||
|
def test_strip_keyword_returns_original_when_not_found():
|
||||||
|
original = "original mix, clean"
|
||||||
|
assert strip_keyword_from_disambiguation(original, "explicit") == original
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"disambiguation, keyword",
|
||||||
|
[
|
||||||
|
("", "explicit"),
|
||||||
|
("original mix", ""),
|
||||||
|
("", ""),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_strip_keyword_handles_empty_inputs(disambiguation, keyword):
|
||||||
|
assert strip_keyword_from_disambiguation(disambiguation, keyword) == disambiguation
|
||||||
Reference in New Issue
Block a user