Docker-сервис, выбор основы и сравнение моделей, сборщик датасета
docker / build (push) Failing after 39s

- Docker: Dockerfile (CPU), compose с FlareSolverr, настройка через DUB_* переменные,
  токен доступа к /predict, tools/pack_models.py, workflow Gitea Actions, docs/docker.md
- Модель грузится офлайн (config.json рядом с весами); общий код вынесен в src/spans.py,
  сервису не нужны sklearn/matplotlib/pandas
- Обучение: профили tiny2/base/large/modern, запуски в models/runs, реестр и честное
  сравнение на одной тестовой выборке (src/runs.py), пункт меню «Модели»
- Датасет: язык и аудио-параметры дорожек, 18+ по строкам «Аудио», ручные метки по ссылкам,
  переиндексация сохранённого HTML, параллельная сборка, сбалансированная выборка
- Тексты страниц (data/pages) больше не хранятся в git

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
DenozordecandClaude Sonnet 5.5 committed 2026-10-28 17:19:10 +07:00
1 parent d155b8584b
commit efc9a4f56a
411 files changed
+87495 -27363

No files matched your search

+187
View File
@@ -0,0 +1,187 @@
# Сбор датасета: `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`), поэтому они
повторяют логику правил. Для нарезки внутри строки («Дублированный,
многоголосый») нужна ручная проверка — режим ревью ещё не сделан.
## Разнообразная выборка (режим 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 → «Пересобрать всё заново».