diff --git a/app/main.py b/app/main.py index 1c5deab..d610684 100644 --- a/app/main.py +++ b/app/main.py @@ -1189,6 +1189,17 @@ def add_tag(image_id): fandom_id = request.form.get("fandom_id", type=int) fandom_name = (request.form.get("fandom_name") or "").strip() or None + # If the user typed the name with a "(Fandom)" suffix and didn't + # use the fandom picker, split the suffix into a real fandom + # association so we don't create a malformed character row with + # the suffix baked into the name and fandom_id=NULL. + import re as _re + if not fandom_id and not fandom_name: + m = _re.match(r'^(.+?) \(([^()]+)\)$', name) + if m: + name = m.group(1).strip() + fandom_name = m.group(2).strip() + if fandom_id: fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first() if fandom_tag is None: @@ -2615,6 +2626,17 @@ def bulk_add_tag(): if kind == "character": fandom_id = data.get("fandom_id") fandom_name = (data.get("fandom_name") or "").strip() or None + + # Same suffix-rescue as /tags/add: typed "Jinx (League Of Legends)" + # without an explicit fandom should still produce a clean character + # row with a real fandom_id, not a malformed name. + import re as _re + if not fandom_id and not fandom_name: + m = _re.match(r'^(.+?) \(([^()]+)\)$', name) + if m: + name = m.group(1).strip() + fandom_name = m.group(2).strip() + if fandom_id: fandom_tag = Tag.query.filter_by(id=fandom_id, kind="fandom").first() if fandom_tag is None: diff --git a/app/utils/tag_prefix.py b/app/utils/tag_prefix.py index ec60cee..81a557b 100644 --- a/app/utils/tag_prefix.py +++ b/app/utils/tag_prefix.py @@ -25,6 +25,11 @@ KNOWN_KINDS: frozenset[str] = frozenset({ def parse_kind_prefix(raw: str) -> tuple[str | None, str]: """Split a raw user-typed tag string into (kind, name). + The kind prefix is matched case-insensitively so 'Character:' and + 'CHARACTER:' resolve the same as 'character:'. The returned kind is + always lowercase canonical form. The modal JS already lowercases + before its own prefix check; this keeps the server in sync. + Returns: (kind, name) where kind is one of KNOWN_KINDS, or (None, raw.strip()) if no recognized prefix is present. @@ -32,12 +37,14 @@ def parse_kind_prefix(raw: str) -> tuple[str | None, str]: Examples: parse_kind_prefix('character:Saber') -> ('character', 'Saber') + parse_kind_prefix('Character:Saber') -> ('character', 'Saber') parse_kind_prefix('sunset') -> (None, 'sunset') parse_kind_prefix('http://example') -> (None, 'http://example') parse_kind_prefix('artist: Eric ') -> ('artist', 'Eric') """ if ':' in raw: prefix, rest = raw.split(':', 1) - if prefix in KNOWN_KINDS: - return prefix, rest.strip() + prefix_lower = prefix.lower() + if prefix_lower in KNOWN_KINDS: + return prefix_lower, rest.strip() return None, raw.strip()