diff --git a/src/html_extract.py b/src/html_extract.py index 1eb588b..257da2a 100644 --- a/src/html_extract.py +++ b/src/html_extract.py @@ -133,7 +133,7 @@ def extract_segments(text: str) -> list[str]: k = i while k < len(lines) and not lines[k].strip(): k += 1 - if k >= len(lines) or not RE_TYPE_START.match(lines[k]): + if k >= len(lines) or not RE_TYPE_START.match(lines[k]) or ":" in lines[k]: # «тип: определение» — глоссарий break body += " ; " + lines[k].strip() i = k + 1 diff --git a/tests/run_tests.py b/tests/run_tests.py index fa32e41..19fff3b 100644 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -823,6 +823,15 @@ check("метки rt_parse: кодек и битрейт из строки «А all(r["codec"] == "AC3" and r["kbps"] == "192" for r in _hr), str(_hr)) check("метки rt_parse: 5 студий + 1 доп. 18+", len(_rows) == 6, str(len(_rows))) +_bare = ("Перевод:\nПрофессиональный (дублированный) | Cinema Tone Production\n\nПрофессиональный (псевдодублированный) | HDRezka Studio\n" + "Профессиональный (многоголосый закадровый) | HDRezka Studio\nПрофессиональный (многоголосый закадровый) | TVShows\n" + "\nОригинальная аудиодорожка: Английская\nСубтитры: Русские, английские\n") +_br, _ = _hc.rt_parse({"id": "6919056", "title": "Астрал 6 [2026, WEB-DL 2160p]", "post": _bare}) +check("метки rt_parse: «Перевод:» без значения + список строк (6919056)", + [(r["translation_type"], r["studio"]) for r in _br] == [ + ("проф. дубляж", "Cinema Tone Production"), ("люб. дубляж", "HDrezka"), # псевдодубляж = «люб. дубляж» + ("проф. многоголосый", "HDrezka"), ("проф. многоголосый", "TVShows")], str([(r["translation_type"], r["studio"]) for r in _br])) + _sf = __import__("src.release", fromlist=["x"]).series_info("Сериал / Сезон 1 / Серии 1-8 из 8 [2026]", [ {"studio": "LostFilm", "episodes": "1-4", "episode_ranges": [[1, 4]]}, {"studio": "HDrezka", "episodes": None, "episode_ranges": []}]) diff --git a/tools/collect.py b/tools/collect.py index 04a96e2..e0b196f 100644 --- a/tools/collect.py +++ b/tools/collect.py @@ -1028,7 +1028,7 @@ def cmd_build(rewrite: bool = False, relabel: bool = False, runner: "Runner | No # Версия разбора: поднимите при правке парсеров (harvest_core, html_to_text) — тогда # кэш «уже разобрано» сбросится сам и данные пересоберутся один раз. -PARSE_VERSION = 3 +PARSE_VERSION = 4 def load_json(path: Path) -> dict: diff --git a/tools/harvest/harvest_core.py b/tools/harvest/harvest_core.py index 55d8900..6164649 100644 --- a/tools/harvest/harvest_core.py +++ b/tools/harvest/harvest_core.py @@ -1288,6 +1288,10 @@ def _kz_parse_core(rec: dict) -> tuple[list[dict], dict]: # ---- rutracker ----------------------------------------------------- RT_RE_FIELD = re.compile( r"^\s*(?:Перевод|Озвучка|Озвучивание|Озвучено|Звук)\s*(?:#\s*\d+|\d+)?\s*:\s*(?P
.+)$", re.I) +RT_RE_BARE = re.compile( + r"^\s*(?:Перевод|Озвучка|Озвучивание|Озвучено|Звук)\s*(?:#\s*\d+|\d+)?\s*:\s*$", re.I) +RT_RE_TYPE_START = re.compile( + r"^\s*(?:Профессиональн|Любительск|Авторск|Дублирован|Многоголос|Двухголос|Одноголос)", re.I) RT_RE_SUBS = re.compile(r"^\s*Субтитры\s*:\s*(?P.+)$", re.I) RT_RE_BRACKET = re.compile(r"\[(?P[^\]]*)\]") RT_RE_SEP = re.compile(r"\s*(?:\+|;|/)\s*(?=Проф|Люб|Авт|Дубл|Многоголос|Двухголос|Одноголос)", re.I) @@ -1366,20 +1370,44 @@ def audio18_rows(rows: list[dict], post: str) -> list[dict]: return rows + extra +def rt_bare_list(lines: list[str], i: int, taken: set[int]) -> list[str]: + """«Перевод:» без значения: дорожки списком ниже, по строке на дорожку. + + Берутся подряд идущие строки со словом типа озвучки; между ними могут стоять + пустые (в HTML после