Files
grabber-ai/src/audio18.py
T
DenozordecandClaude Sonnet 5.5 05537ac9e9
docker / build (push) Successful in 1m40s
Датасет и обучение: класс «люб. дубляж», раскладка многотиповых строк, язык из «Аудио», строки из «Аудио», потолок повторов, веса классов
- harvest_core: дубляж — тип, а не часть имени студии; split_multi_rows; audio_only_rows; kinozal сопоставляет все аудиодорожки
- prepare_data: --max-dup, отсев остатков многотиповых строк
- train: веса классов (корень из обратной частоты, потолок 4), --no-class-weights
- исправлены регулярки с \b, записанные как символ BS (predict.py, audio18.py)
- PARSE_VERSION 3: кэш разбора сбрасывается
- docs/dataset.md, docs/api-reference.md обновлены

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-29 18:13:07 +07:00

91 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""18+ из строк «Аудио … |Студия| 18+».
На rutracker/nnm пометка 18+ часто стоит не в строке «Перевод:», а только в
строке «Аудио», причём у студии может быть и обычная дорожка, и 18+:
Перевод: Профессиональный (многоголосый закадровый) HDRezka
Аудио: RUS: AC3, 2 ch, 192 kbps |HDrezka|
Аудио: RUS: AC3, 2 ch, 192 kbps |HDrezka| 18+
Модель видит только строку дорожки, поэтому 18+ из соседней строки ей не
достать: он добавляется здесь, по тексту страницы.
"""
from __future__ import annotations
import re
RE_AUDIO_LINE = re.compile(r"^\s*Аудио\b.*$", re.I | re.M)
RE_ADULT = re.compile(r"(?<!\d)18\s*\+")
_NORM = re.compile(r"[\W_]+")
def _norm(s: str) -> str:
return _NORM.sub("", (s or "").casefold())
RE_CODEC = re.compile(r"(E-?AC-?3|[AА][CС]-?3|AAC|DTS-?HD|DTS|FLAC|TrueHD|Opus|MP3)", re.I)
RE_CH = re.compile(r"(\d(?:\.\d)?)\s*ch\b", re.I)
RE_KHZ = re.compile(r"(\d+(?:\.\d+)?)\s*kHz", re.I)
RE_KBPS = re.compile(r"(\d+)\s*kbps", re.I)
def audio_lines(text: str, studios: list[str]) -> list[dict]:
"""Строки «Аудио …», в которых названа одна из студий, с параметрами дорожки."""
names = {_norm(s): s for s in studios if len(_norm(s)) >= 3}
out = []
for m in RE_AUDIO_LINE.finditer(text or ""):
line = m.group(0)
flat = _norm(line)
hits = [k for k in names if k in flat]
if not hits:
continue
g = lambda rx: (rx.search(line).group(1) if rx.search(line) else "") # noqa: E731
out.append({"studio": names[max(hits, key=len)], # «Red Head Sound», а не «Red»
"adult": bool(RE_ADULT.search(line)),
"codec": g(RE_CODEC).upper().replace("А", "A").replace("С", "C"),
"channels": g(RE_CH), "khz": g(RE_KHZ), "kbps": g(RE_KBPS)})
return out
def audio_counts(text: str, studios: list[str]) -> dict[str, list[int]]:
"""{студия: [строк Аудио с 18+, строк Аудио без 18+]} для названных студий."""
out: dict[str, list[int]] = {}
for a in audio_lines(text, studios):
out.setdefault(a["studio"], [0, 0])[0 if a["adult"] else 1] += 1
return out
def apply_audio_18(result: dict, text: str) -> dict:
"""Дополняет результат релиза: 18+ из строк «Аудио» (на месте)."""
tracks = result.get("tracks") or []
studios = sorted({t["studio"] for t in tracks if t.get("studio")})
if not studios:
return result
counts = audio_counts(text, studios)
out: list[dict] = []
for t in tracks:
out.append(t)
for studio, (adult, plain) in counts.items():
if not adult:
continue
mine = [t for t in out if t.get("studio") == studio]
if not mine or any(t["is_18plus"] for t in mine):
continue
if plain: # есть и обычная, и 18+ дорожка -> отдельная 18+
base = mine[0]
extra = dict(base)
extra["is_18plus"] = True
extra["segment"] = f"{base['segment']} 18+"
extra["source"] = "audio"
out.insert(out.index(base) + 1, extra)
else: # все дорожки студии 18+
for t in mine:
t["is_18plus"] = True
result["tracks"] = out
result["studios"] = [{"studio": t["studio"], "translation_type": t["translation_type"],
"lang": t.get("lang"), "is_18plus": t["is_18plus"], "episodes": t.get("episodes")}
for t in out if t.get("studio")]
result["is_18plus"] = any(t["is_18plus"] for t in out)
return result