* harvest_core.nnm_part_studio: студия из части строки «Перевод:» nnm («Дублированный (Невафильм)», «… - Jaskier»); раньше такие строки шли с «(не указана)», и модель училась не извлекать студию даже из «Дублированный (Пифагор)». tech-строки со студиями больше не выбрасываются, если на странице нет строк «Аудио N» с именами. * harvest_core.kz_title_rows: kinozal, студии из заголовка «ПМ (LostFilm, HDrezka Studio)», которых нет на странице; вход — сегмент как у сервиса, строка «Аудио Rus: … | Студия» — в audio_raw. * tools/relabel_studios.py — то же по готовому labels.csv без пересборки страниц; применено к data/labels.csv: со студией 22 061 -> 29 331 строк (nnm +3 051, kinozal +4 219 строк в 3 007 релизах); prepare_data: 39 418 строк, спан студии 24 004 -> 31 214. * e2e: эталон как aggregate — «тип без студии» убирается, если у типа есть студия. Co-Authored-By: Claude Opus 5.5 <[email protected]>
205 lines
17 KiB
Markdown
205 lines
17 KiB
Markdown
# Сбор датасета: `tools/collect.py`
|
||
|
||
Интерактивный сборщик страниц rutracker / nnm-club / kinozal. Логин делает
|
||
человек в обычном окне Chrome, скрипт пароли и cookie не видит.
|
||
|
||
```powershell
|
||
.venv\Scripts\python tools\collect.py # меню
|
||
```
|
||
|
||
Главное меню состоит из двух пунктов и подменю:
|
||
|
||
1. **Собрать датасет**: окна, вход, сбор (новые и/или дособрать размеченные),
|
||
`build`, `prepare_data`.
|
||
2. **Переобучить и протестировать**: `prepare_data`, обучение сегментатора в
|
||
`models/segmenter_v2` (по желанию ещё и основной модели), `tests/run_tests.py`,
|
||
таблица «правила / старые веса / новые веса» по трекерам и вывод, лучше ли
|
||
новые; подмена весов только после вашего «y» (старые уходят в
|
||
`models/segmenter_old_<время>`).
|
||
3. Дополнительно: отдельные шаги из таблицы ниже.
|
||
|
||
Подменю «Дополнительно»:
|
||
|
||
| Пункт | Что делает |
|
||
|---|---|
|
||
| 1 | Запускает Chrome (один процесс, общий профиль), открывает по **окну на трекер**, ждёт Enter, проверяет статус входа (вошли / гость / не определено) |
|
||
| 2 | Новые раздачи из списков разделов (`viewforum.php?f=…`, для kinozal — адрес списка). По потоку на трекер, у каждого своё окно. Продолжает с места остановки |
|
||
| 3 | Дособрать страницы для раздач, которые **уже размечены, но страницы нет** (nnm, kinozal) |
|
||
| 4 | `data/raw` → `data/pages/*.txt` + новые строки в `data/labels.csv` |
|
||
| 5 | Импорт старого harvest без браузера (только текст поста) |
|
||
| 6 | Статус по трекерам |
|
||
|
||
Те же операции без меню: `windows`, `collect`, `backfill`, `build`,
|
||
`import-legacy`, `status` (см. `python tools/collect.py -h`).
|
||
|
||
## Интерфейс
|
||
|
||
Главное меню (rich) показывает сводку (размечено, страниц, meta, модели, порт
|
||
Chrome). Пункт 3 — статистика по трекерам: покрытие и состав датасета (тип,
|
||
разрешение, кодек, годы, языки). Во время сбора идёт живая панель: по каждому
|
||
трекеру состояние, прогресс, сохранено, ошибки, скорость (стр/мин по последним
|
||
сохранениям), время, ETA, текущий раздел и последняя раздача. `Ctrl+C`
|
||
останавливает сбор после текущей раздачи.
|
||
|
||
## Метаданные релизов: `data/meta.csv`
|
||
|
||
`build` (и команда `meta`) строит из сохранённых страниц таблицу, одна строка на
|
||
релиз (`tools/meta.py`; заходить на трекеры повторно не нужно):
|
||
|
||
| Группа | Поля |
|
||
|---|---|
|
||
| Название | `title`, `ru_title`, `orig_title`, `year`, `kind` (film / series / cartoon / anime), `is_series`, `season`, `episodes`, `episodes_total`, `section` |
|
||
| Видео | `quality`, `resolution`, `video_codec`, `hdr`, `bit_depth`, `fps`, `container`, `size_gb`, `duration_min` |
|
||
| О фильме | `countries`, `genres`, `directors`, `cast`, `age_limit`, `mpaa`, `release_group`, `source_media` |
|
||
| Звук | `audio_langs`, `orig_audio`, `n_audio`, `audio_tracks` (JSON: язык, кодек, каналы, битрейт, тип DUB/MVO/DVO/AVO/VO/ORIG, студия, серии, 18+) |
|
||
| Субтитры | `sub_langs`, `sub_forced`, `sub_format` |
|
||
|
||
Для старых данных (импорт `import-legacy`) поля берутся из текста поста;
|
||
размер раздачи rutracker и nnm виден только на полной странице, поэтому у них он
|
||
заполнится после дозагрузки (пункт «Дособрать»).
|
||
|
||
## Переиндексация без перекачки
|
||
|
||
HTML страниц хранится в `data/raw`, поэтому после правки парсеров данные
|
||
пересобираются без захода на трекеры: пункт 2 «Переобучить» начинается с вопроса
|
||
«Переиндексировать data/raw?» (то же: `collect.py build --rewrite --relabel`).
|
||
Страницы, `meta.csv` и метки размеченных релизов строятся заново текущими
|
||
парсерами; прежний `labels.csv` копируется в `data/labels.bak_<время>.csv`. Не
|
||
трогаются ручные метки (пункт 6), релизы без HTML (импорт старого harvest) и
|
||
релизы, по которым парсер ничего не нашёл.
|
||
|
||
## Обучение по ссылкам
|
||
|
||
Пункт 6 главного меню: вставляете ссылки на раздачи (rutracker / nnm / kinozal),
|
||
страницы скачиваются вашим Chrome, парсер строит метки, вы правите их в таблице
|
||
(«2 18» — переключить 18+, «3 d» — удалить, «1 t <тип>», «1 s <студия>») и
|
||
сохраняете. Метки заменяют прежние строки релиза в `data/labels.csv`
|
||
(`studio_source=manual`), id попадает в `data/manual_ids.txt`: такие релизы
|
||
всегда в train и повторяются при обучении ×5. Дальше «Переобучить».
|
||
|
||
Метки и инференс берут 18+ и параметры дорожки (кодек, каналы, битрейт) не только
|
||
из строк «Перевод», но и из строк «Аудио … |Студия| 18+» (`src/audio18.py`): у
|
||
одной студии может быть обычная дорожка и дорожка 18+.
|
||
|
||
## Автопоиск разделов
|
||
|
||
При первом запуске пункта 1 (режим «новые раздачи») после входа в аккаунты
|
||
приложение само находит разделы с зарубежными фильмами и сериалами
|
||
(`tools/discover.py`) и кэширует их в `data/raw/_sections.json`:
|
||
|
||
* **rutracker, nnm**: разделы выбираются по названию (зарубежное кино и сериалы,
|
||
мультфильмы, новинки, классика, годовые «Фильмы 2021-2025»; наше кино,
|
||
азиатское, музыка, Apple, коллекции исключены). Форматы DVD / HD / UHD / 3D
|
||
и годы покрываются самими разделами.
|
||
* **kinozal**: ваши списки «все фильмы» (`c=1002`) и «сериалы» (`c=46`), затем
|
||
сетка год × формат × категория (4K только с 2012 и т. д.).
|
||
* Лимит делится между разделами поровну, чтобы выборка не состояла из одного.
|
||
* Раздел, который не удалось проверить, остаётся в списке; результат неполной
|
||
загрузки кэш не затирает. Искать заново: «Дополнительно» → 7 или
|
||
`collect.py discover --force`.
|
||
|
||
## Сохранённые страницы читаемы
|
||
|
||
В `data/raw/<трекер>/<id>.html` лежит снимок страницы «как на сайте»: UTF-8
|
||
(`<meta charset>`), `<base>` на адрес раздачи (картинки подгружаются с сайта),
|
||
таблицы стилей вынесены в `data/raw/_css/<хеш>.css` (общие для всех страниц),
|
||
скрипты убраны, чтобы страница из файла не уходила на сайт. Открывается двойным
|
||
кликом. Таблицы стилей чужого домена (rutracker, nnm) остаются ссылкой на сайт и
|
||
работают при наличии интернета.
|
||
|
||
Страницы, сохранённые раньше, чинит `python tools/collect.py repair-html`
|
||
(кодировка, `<base>`, стили сайта одной загрузкой со страницы трекера; нужен Chrome
|
||
и вход, `--no-css` — без браузера). Время файлов не меняется, кэши разбора
|
||
остаются.
|
||
|
||
## Где что лежит
|
||
|
||
* `data/raw/<трекер>/<id>.html|json` — страница целиком (outerHTML после
|
||
загрузки вкладок kinozal) и поля для парсера. В git не попадает.
|
||
* Профиль Chrome — `%LOCALAPPDATA%\dub-ai-harvest\chrome-profile`, вне
|
||
репозитория (`DUB_CHROME_PROFILE`, `DUB_CHROME_PORT` меняют каталог и порт).
|
||
* `tools/harvest/harvest_core.py` — перенесённый `dataset.py` (CDP-клиент и
|
||
парсеры меток), единственная правка — переменные окружения для путей.
|
||
|
||
## id страниц
|
||
|
||
`release_id` в `labels.csv` совпадает с именем `data/pages/<id>.txt`.
|
||
Раздачи, размеченные раньше, сохраняют прежний числовой id (их разбиение
|
||
train/test не меняется); новые получают префикс `rt-`, `nnm-`, `kz-`, чтобы
|
||
id разных трекеров не пересекались. Коллизий в текущих данных нет, но один id
|
||
уже встречался в сырых данных двух трекеров.
|
||
|
||
## Полные и урезанные страницы
|
||
|
||
Импорт старого harvest (пункт 5) даёт страницу из сохранённого текста поста:
|
||
без шапки сайта, соседних раздач и заголовка. Такие страницы перечислены в
|
||
`data/pages_postonly.txt`. При обучении и оценке сегментатора их стоит
|
||
считать отдельно: на инференсе модель видит полную страницу.
|
||
|
||
## Что в метках слабое
|
||
|
||
Метки строятся парсерами (`tools/harvest/harvest_core.py`), поэтому они
|
||
повторяют логику правил. Для нарезки внутри строки («Дублированный,
|
||
многоголосый») нужна ручная проверка — режим ревью ещё не сделан.
|
||
|
||
Студии, которые парсеры раньше теряли (доразметка от 02.11.2026):
|
||
|
||
* **nnm, «Перевод: Дублированный (Невафильм)»** — строки «Перевод:» (tech) шли с
|
||
«(не указана)», хотя студия названа в той же части. Модель училась её не извлекать.
|
||
Теперь — `nnm_part_studio` (скобки или «… - Студия»; тип, «x2», «Лицензия», источник
|
||
вроде iTunes/Blu-ray — не студия). +3 051 студия.
|
||
* **kinozal, заголовок «… / ПМ (LostFilm, HDrezka Studio), СТ / …»** — во «Техданных»
|
||
только «Перевод: Профессиональный многоголосый», студии названы в заголовке и в
|
||
строках «Аудио Rus: … | LostFilm» без типа. Теперь — `kz_title_rows`: строка на каждую
|
||
студию из заголовка, которой нет на странице (`studio_source=title`, вход — тот же
|
||
сегмент, что строит сервис из заголовка; строка «Аудио» с этой студией — в
|
||
`audio_raw`). +4 219 строк в 3 007 релизах.
|
||
|
||
Сборщик делает это при переиндексации. Для уже собранного `labels.csv` без пересборки
|
||
страниц: `.venv\Scripts\python tools\relabel_studios.py` (копия — `data/labels.bak_relabel_*.csv`;
|
||
`--dry-run` — только статистика; повторный запуск ничего не добавляет).
|
||
|
||
## Разнообразная выборка (режим balanced)
|
||
|
||
Списки разделов идут от новых к старым, поэтому чтение подряд даёт только свежие
|
||
релизы. По умолчанию (`Настройки` → режим `balanced`, `tools/diversity.py`):
|
||
* страницы раздела берутся **вразброс по всей глубине** (число страниц читается из
|
||
пагинации; посещённые запоминаются в `_state.json`, круг по разделу продолжается
|
||
с места остановки);
|
||
* с каждой страницы берётся не всё, а `per_page` тем (по умолчанию 6) из **самых
|
||
малочисленных корзин** «год (до 2010 — десятилетие) × качество (4K / 1080 / 720 /
|
||
SD) × фильм/сериал»; уже собранное считается по `data/meta.csv`;
|
||
* режим `sequential` — прежнее чтение подряд.
|
||
|
||
## Скорость переиндексации
|
||
|
||
Разбор HTML (страницы, метки, `meta.csv`) идёт в нескольких процессах: по умолчанию
|
||
число ядер минус два (до 12); меняется в «Настройки» (`workers`, 0 — авто, 1 — без
|
||
процессов). `meta.csv` берёт текст из уже записанных `data/pages`, а не разбирает
|
||
HTML второй раз. На выборке в 600 раздач — 45 с → 8 с при тех же результатах.
|
||
|
||
## Если сбор «завис»
|
||
|
||
* В панели у трекера теперь видно номер раздачи, которая качается: `скачиваю 6894333`.
|
||
* Короткая страница (закрытая или перенесённая тема), которая загрузилась и не меняется,
|
||
принимается через ~5 с; раньше сборщик ждал таймаут 45 с и повторял его.
|
||
* Раздача дольше 40 с попадает в ошибки как «медленно, N с» (id можно открыть вручную).
|
||
* Сторож: раздача дольше 150 с — вызов к вкладке обрывается, вкладка переподключается, сбор
|
||
идёт дальше.
|
||
|
||
## Скорость и повторный разбор
|
||
|
||
* **Вкладки.** Сбор идёт в нескольких вкладках одного трекера (общий Chrome, общий
|
||
вход). Число вкладок — пункт меню «Настройки» (1–4, по умолчанию 3), запоминается;
|
||
в командах `collect` / `backfill` флаг `--tabs`. Больше вкладок — быстрее, но выше
|
||
риск лимитов и проверок Cloudflare: при ошибках снизьте до 1–2.
|
||
* **Без лишних пауз.** Ожидание вкладок kinozal и загрузки страниц опрашивает
|
||
состояние, а не спит фиксированное время; тяжёлые темы не ждут `complete`;
|
||
удалённые раздачи определяются сразу и запоминаются (`data/raw/_dead.json`).
|
||
* **Ничего не разбирается дважды.** Сбор пропускает уже сохранённые, размеченные и
|
||
удалённые раздачи и продолжает списки с места остановки. `build` запоминает
|
||
«подпись» файлов (`data/raw/_built.json`, `_meta_cache.json`): повторный запуск
|
||
разбирает только новое и изменённое (на 2800 релизах: 59 с → 2 с). Кэш сбрасывается
|
||
сам, если поднять `PARSE_VERSION` (collect.py) или `META_VERSION` (meta.py) после
|
||
правки парсеров; вручную: «Дополнительно» → 4 → «Пересобрать всё заново».
|