"""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"(? 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