fix(matching): keep abbreviation titles out of bare-year keys (#1426)

A leading 2-5 character uppercase token before a dash, pipe or colon was
always read as a provider tag, so a film whose NAME is such a token lost it and
normalized down to its release year alone. "AKA - 2023" became the key "2023",
where it collided with BDE, BRO, OUT, WIL and IF — and they were offered to
each other as alternative sources in VOD multi-source.

"IT - 65 (2023)" (the Italian copy of the film "65") is structurally identical,
so only the token's meaning can separate them. The leading token is now tested
against a vocabulary — TRAILING_TAG_VOCABULARY plus a prefix-only list derived
from the real catalog — but only when the strip would leave no real word
behind. A compound is read by its head, so the open-ended "4K-<lang>" family
keeps working while "INU-OH" and "PC-4L" are recognized as film names. An
unknown token keeps its title: a refused strip costs one unmatched copy, a
wrong one corrupts that film's identity in VOD multi-source, the TMDB Similar
rail, DB_MATCH_TITLES and pin keys.

"No real word" is decided by running the rest of the pipeline on the stripped
form and looking at what comes out, never by re-implementing what later stages
remove. Quality tags, trailing and underscore tags, double-dash suffixes and
season markers each otherwise smuggle the strip through, and a stage added
later is covered for free.

Validated over the live catalog, movies and series: 83 keys fixed, 0 corrupted
across 1,616,111 titles. Deriving the vocabulary from movies alone missed AMZ,
D+ and P+ and broke the Paramount+/Disney+ copies of the numeric series 1923,
1883, 24 and 9-1-1.
This commit is contained in:
4gray authored and GitHub committed 2026-08-13 17:01:33 +02:00
1 parent 30d76a3a46
commit 82a2948ffd
5 files changed
+379 -26

No files matched your search

+44
View File
@@ -216,6 +216,50 @@ reader is free to be permissive because the gate in front of its riskier
forms — and the fact that a row only appears once it HAS matched — keeps a
bad guess cosmetic.
The one case where the shape genuinely cannot decide is a strip that would
leave **no real word behind**. "IT - 65 (2023)" is the Italian copy of the film
"65"; "AKA - 2023" is the film "AKA" and its year. Same shape, opposite
readings, so the leading token is tested against a vocabulary instead —
`TRAILING_TAG_VOCABULARY` plus a prefix-only list (`NF`, `EX`, `NRC`, `AMZ`,
`D+`, `P+`, `OSN`, `VO`, …), with a compound read by its HEAD so the
open-ended `4K-<lang>` family works and "INU-OH"/"PC-4L" — real film names —
do not. Unknown token, wordless remainder: the title is kept whole. That
direction is chosen deliberately, because a refused strip costs one unmatched
copy while a wrong one produced a bare-year key; on the live catalog
AKA/BDE/BRO/OUT/WIL/IF all collapsed onto `"2023"` and were offered to each
other as alternative sources. Gating the case where a word DOES survive was
rejected for the mirror reason: it would strand every genuine tag the
vocabulary has not heard of.
"No real word" is decided by running the REST OF THE PIPELINE on the stripped
form and looking at what comes out — never by re-implementing what the later
stages remove. That is the load-bearing part of the design: every stage drops
something, so a guard that predicts them is a list to keep in sync, and each
omission is a silent collapse:
| title | dropped by | would key as |
| ------------------- | ------------------ | ------------ |
| `\|TA\| RRR - HEVC` | quality tag | `""` |
| `CAT - Multi ENG` | trailing tag | `""` |
| `IF - 2024_sub` | underscore tag | `2024` |
| `AKA --xyz` | double-dash suffix | `""` |
| `CAT - 2022 S01` | season marker | `2022` |
All five fall out of one question asked of the real output, and a stage added
later is covered for free. The season check deliberately uses
`SEASON_SUFFIX_PATTERN` directly rather than `stripSeason`, whose
"never return empty" fallback would report a lone season marker as a surviving
word. A tag word sitting next to a real one is still part of the title
("EN - Sub Zero" → `sub zero`).
The vocabulary is evidence, not intuition. Each entry prefixes hundreds to
thousands of ordinary lettered titles in the real catalog; nothing is added
because it "looks like a streaming service" (MAX and HULU would qualify, and
"MAX - 2015" is a film). Deriving it from movies alone missed `AMZ`, `D+` and
`P+`, which broke Paramount+/Disney+ copies of the numeric series 1923, 1883,
24 and 9-1-1 — so validate any change over movies AND series: 83 keys fixed,
0 corrupted across 1,616,111 titles.
The category path exists because many panels tag the CATEGORY ("EN | Netflix",
"DE | Apple TV") and leave stream titles bare. Discovery returns every visible
category name a stream sits in, and the two query tiers get there