diff --git a/src/html_extract.py b/src/html_extract.py
index 257da2a..bbf7226 100644
--- a/src/html_extract.py
+++ b/src/html_extract.py
@@ -51,6 +51,9 @@ RE_SEP = re.compile(
RE_TYPE_START = re.compile(
r"^\s*(?:Профессиональн|Любительск|Авторск|Дублирован|Многоголос|Двухголос|Одноголос)", re.I)
+# scene-имя релиза «A.b.c.2005.2160p.UHD.Blu-ray.Remux-GROUP»: без пробелов, ≥ 4 точек; дорожкой не бывает
+RE_SCENE = re.compile(r"^\S+(?:\.\S+){4,}$")
+
SELECTORS = ("div.post_body", "div[id^=post-text-]", ".postbody", "#tabs")
diff --git a/src/segmenter.py b/src/segmenter.py
index e1cef25..38d7612 100644
--- a/src/segmenter.py
+++ b/src/segmenter.py
@@ -34,7 +34,7 @@ import numpy as np
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer
-from .html_extract import read_input
+from .html_extract import RE_SCENE, read_input
BACKBONE = "cointegrated/rubert-tiny2"
MAX_LEN = 160
@@ -189,8 +189,8 @@ def build_examples(split: str, rng: random.Random, synth_per_page: int = 6) -> l
continue # неразмеченная кодек-строка: ни позитив, ни негатив
prev = lines[i - 1] if i else ""
nxt = lines[i + 1] if i + 1 < len(lines) else ""
- if not y and not HINT.search(line) and rng.random() > 0.15:
- continue # простые негативы прореживаем, трудные (с намёками) берём все
+ if not y and not HINT.search(line) and not RE_SCENE.match(line) and rng.random() > 0.15:
+ continue # простые негативы прореживаем, трудные (с намёками, scene-имена релизов) берём все
ex.append((*make_input(prev, line, nxt), y))
if not want_test:
negs = [l for l, y in zip(lines, labels) if y == 0]
@@ -231,7 +231,7 @@ class LineSegmenter:
if not lines:
return []
probs = self.score(lines)
- return [l for l, p in zip(lines, probs) if p >= threshold]
+ return [l for l, p in zip(lines, probs) if p >= threshold and not RE_SCENE.match(l)]
# ---------------------------------------------------------------- обучение
diff --git a/tests/run_tests.py b/tests/run_tests.py
index 19fff3b..9ee7d5d 100644
--- a/tests/run_tests.py
+++ b/tests/run_tests.py
@@ -16,7 +16,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
-from src.html_extract import (RE_LINE_PREFIX, _split_entries, # noqa: E402
+from src.html_extract import (RE_LINE_PREFIX, RE_SCENE, _split_entries, # noqa: E402
extract_segments, html_to_text)
from src.studio_norm import canonical, find_studio_span # noqa: E402
@@ -151,6 +151,16 @@ check("предупреждение: модель пуста, правила н
check("предупреждение: правила нашли больше", "больше" in (segmenter_warning(True, "model", ["a"], ["a", "b"]) or ""))
check("без предупреждения: совпало / нет модели",
segmenter_warning(True, "model", ["a"], ["a"]) is None and segmenter_warning(False, "rules", [], ["x"]) is None)
+check("scene-имя релиза — не дорожка (kinozal 2160145)",
+ RE_SCENE.match("Memoirs.of.a.Geisha.2005.2160p.UHD.Blu-ray.Remux.DV.HDR.HEVC.TrueHD.Atmos.7.1-CiNEPHiLES")
+ and not RE_SCENE.match("Профессиональный (многоголосый закадровый) | HDRezka Studio")
+ and not RE_SCENE.match("Аудио 1: Русский (DTS-HD MA, 6 ch, 2240 Кбит/с) / Дублированный"))
+from src.segmenter import LineSegmenter # noqa: E402
+import numpy as _np # noqa: E402
+_seg = object.__new__(LineSegmenter)
+_seg.score = lambda lines: _np.ones(len(lines)) # модель «выбирает» всё
+_got = _seg.select_lines("Перевод: Дублированный\nMemoirs.of.a.Geisha.2005.2160p.UHD.Remux-GRP\nАудио 1: Русский / Дублированный")
+check("select_lines отбрасывает scene-имя даже при оценке 1.0", len(_got) == 2 and all("Geisha" not in x for x in _got), str(_got))
# ---------------------------------------------------------------- html
print("== html_to_text ==")