Студии из кодек-строк: codec_line_rows, доразметка, аудит дорожек

tools/audit_tracks.py и reports/tracks_audit.md — аудит всех дорожек
техданных по data/raw с планом. Пункт 1 плана: harvest_core.codec_line_rows
добавляет студии из строк «Аудио 2: AC3 … (MVO, LostFilm)», «Аудио Rus: … |
TVShows» (studio_source=codec), которых нет среди строк релиза; тот же шаг
в tools/relabel_studios.py (повторный запуск ничего не добавляет).
labels.csv: +6 428 строк в 2 862 релизах (kinozal 3 444, nnm 1 609,
rutracker 1 375), со студией 29 331 -> 35 759. tests/e2e_segmenter.py:
модель через E2E_MODEL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-11-02 20:30:52 +07:00
1 parent 63878f7874
commit e3af778068
10 files changed
+7937 -29

No files matched your search

+36 -3
View File
@@ -6,7 +6,9 @@
* nnm, строки «Перевод:» (studio_source=tech) с «(не указана)»: студия, названная
в самой части строки («Дублированный (Невафильм)», «… - Jaskier») — nnm_part_studio;
* kinozal: студии из заголовка раздачи «ПМ (LostFilm, HDrezka Studio)», которых нет
среди строк релиза (studio_source=title) — kz_title_rows по data/raw/kinozal/<id>.json.
среди строк релиза (studio_source=title) — kz_title_rows по data/raw/kinozal/<id>.json;
* все трекеры: студии из кодек-строк «Аудио 2: AC3 … (MVO, LostFilm)», «Аудио Rus: … | TVShows»,
которых нет среди строк релиза (studio_source=codec) — codec_line_rows по data/raw/<трекер>/<id>.json.
Ручные релизы (data/manual_ids.txt) не трогаются. Перед записью — копия
data/labels.bak_relabel_<время>.csv. Повторный запуск ничего не добавляет.
@@ -20,6 +22,7 @@ from __future__ import annotations
import argparse
import csv
import json
import re
import shutil
import sys
import time
@@ -33,7 +36,8 @@ sys.path.insert(0, str(ROOT / "tools" / "harvest"))
import harvest_core as H # noqa: E402
LABELS = ROOT / "data" / "labels.csv"
RAW_KZ = ROOT / "data" / "raw" / "kinozal"
RAW = ROOT / "data" / "raw"
RAW_KZ = RAW / "kinozal"
MANUAL = ROOT / "data" / "manual_ids.txt"
NO_STUDIO = "(не указана)"
@@ -70,7 +74,36 @@ def relabel(rows: list[dict], raw_kz: Path = RAW_KZ, manual: set[str] | None = N
stats["kinozal: студий из заголовка"] += len(new)
stats["kinozal: из них со строкой «Аудио»"] += sum(bool(x["audio_raw"]) for x in new)
added += new
return rows + added, stats
rows = rows + added
by_rel = defaultdict(list)
for r in rows:
by_rel[(r["source"], r["release_id"])].append(r)
codec: list[dict] = []
for (src, rid), rel_rows in by_rel.items():
if rid in manual:
continue
text = raw_text(raw_kz.parent, src, rid)
if text is None:
continue
new = H.codec_line_rows({"id": rid, "source": src}, text, rel_rows)
if new:
stats[f"{src}: релизов со студиями из кодек-строк"] += 1
stats[f"{src}: студий из кодек-строк"] += len(new)
codec += new
return rows + codec, stats
def raw_text(raw_dir: Path, src: str, rid: str) -> str | None:
"""Текст страницы, как у сборщика: kinozal — «Релиз» + «Техданные», nnm/rutracker — пост."""
num = re.sub(r"^(kz|nnm|rt)-", "", rid)
for name in (f"{num}.json", f"{num}.legacy.json"):
path = raw_dir / src / name
if path.exists():
rec = json.loads(path.read_text(encoding="utf-8"))
if src == "kinozal":
return "\n".join(x for x in (rec.get("release_tab") or "", rec.get("tech_tab") or "") if x)
return rec.get("post") or ""
return None
def main() -> None: