Workflow процесса сканирования и записи на диск
Основной процесс сканирования
1. Запуск сканирования
↓
2. Инициализация (TMDBClient, MediaDatabase, MediaScanner)
↓
3. Базовое сканирование (сканируются папки "фильмы" и "сериалы")
↓
4. Классификация медиа через TMDB + Gemini (PersistentGenreClassifier)
↓
5. Поиск дубликатов между дисками (db.update_duplicates)
↓
6. Назначение порядковых номеров (db.assign_numbers)
↓
7. Глубокое сканирование сериалов (эпизоды)
↓
8. Поиск торрентов и исправление путей
↓
9. Генерация отчётов (JSON и Markdown)
↓
10. Аудит (сверка с физическим наличием)
↓
11. Назначение категорий и меток торрентам
↓
12. Отчет о дубликатах
↓
13. Открытие отчёта в браузере
Модули
| Модуль | Назначение |
|---|---|
media_scanner.py |
Классы TMDBClient и MediaScanner для сканирования файловой структуры и получения данных из TMDB API |
media_auditor.py |
Класс MediaAuditor для сверки данных БД с физическим наличием файлов на диске и состоянием торрентов |
genre_classifier.py |
Классы GenreClassifier и PersistentGenreClassifier для классификации медиа по жанрам через TMDB и Gemini |
report_generator.py |
Функции export_disk_json, export_disk_md для экспорта данных из БД в JSON и Markdown |
media_rebuild.py |
Функция rebuild_db для консолидации дублирующихся записей в БД |
media_tracker.py |
Утилиты для фильтрации путей, поиска торрентов, назначения категорий |
Структура базы данных
Правило: Структура записи определяется полем media_type — [movie, serial, season, episode]
Логическая иерархия: episode → season → serial (эпизод принадлежит сезону, сезон принадлежит сериалу)
Связи:
- Для season: parent_id указывает на id сериала
- Для episode: parent_id указывает на id сезона
Структура таблицы media:
| Поле | Тип | Описание |
|---|---|---|
disk_name |
TEXT | Имя диска |
path |
TEXT | Полный путь к файлу/папке |
number |
INTEGER | Порядковый номер |
title |
TEXT | Название медиа |
title_orig |
TEXT | Оригинальное название |
title_ru |
TEXT | Русское название |
type |
TEXT | Тип: movie, series, season, episode |
year |
INTEGER | Год |
main_category |
TEXT | Основная категория |
country |
TEXT | Страна |
genres |
TEXT | JSON-массив жанров |
directors |
TEXT | JSON-массив режиссёров |
cast |
TEXT | JSON-массив актёров |
num_of_seasons |
INTEGER | Количество сезонов (для сериалов) |
num_episodes_per_season |
TEXT | JSON-массив количества серий по сезонам |
status |
TEXT | Статус (для сериалов) |
rating |
TEXT | JSON-объект с оценками (IMDb, TMDB) |
awards |
TEXT | JSON-массив наград |
plot |
TEXT | Сюжет |
atmosphere |
TEXT | Атмосфера |
why_watch |
TEXT | Почему стоит смотреть |
mood |
TEXT | Настроение |
final_verdict |
TEXT | Финальный вердикт |
can_stop_at |
TEXT | Можно остановиться после (сезон/серия) |
quote |
TEXT | Цитата |
facts |
TEXT | JSON-массив интересных фактов |
similar |
TEXT | JSON-массив похожих медиа |
parent_id |
INTEGER | ID родительского элемента (для season/episode) |
episode_scan_skipped |
INTEGER | 1 если сканирование эпизодов пропущено для длинного сериала, 0 — если выполнено |
Правила заполнения полей по типам:
| Тип | num_of_seasons |
num_episodes_per_season |
parent_id |
|---|---|---|---|
movie |
игнорируется | игнорируется | 0 или NULL |
series |
количество сезонов сериала | JSON-массив количества серий по сезонам | 0 или NULL |
season |
игнорируется | JSON-массив количества серий сезона (обычно [n]) |
ID сериала |
episode |
игнорируется | игнорируется | ID сезона |
Примечания:
- Для movie и series поле parent_id равно 0 или NULL
- Все JSON-поля хранятся как TEXT в SQLite и автоматически десериализуются при чтении
Взаимодействие с моделью Gemini
Логика запросов:
1. Для фильма (movie)
Запрос: "Напиши подробную карточку фильма [название]"
Ожидаемый формат: JSON как в примере фильма в instruction.md
2. Для сериала (series)
Запрос: "Напиши подробную карточку сериала [название] (все сезоны)"
Ожидаемый формат: JSON как в примере сериала в instruction.md, с массивом seasons
Умное сканирование эпизодов:
- Система проверяет количество сезонов и общее количество эпизодов через TMDB API
- Если сезонов > 15 ИЛИ общее количество эпизодов > 100, или сериал является длинным ежедневным шоу:
- Запрос детальных эпизодов к Gemini пропускается (или переключается на overview)
- В БД сохраняется episode_scan_skipped = true
- Аудит пропускает проверку количества эпизодов
3. Для сезона (season)
Запрос: "Напиши подробную карточку [номер] сезона сериала [название]"
Ожидаемый формат: JSON с полями:
- Все поля как для фильма (title, plot, atmosphere, rating, facts, similar, review и т.д.)
- season_number — номер сезона
- episodes — массив эпизодов с episode_number, begins, ends, final_verdict
- final_verdict — вердикт по сезону
- parent_id указывает на ID сериала
4. Для эпизода (episode)
Запрос: "Напиши подробное описание [номер] эпизода [номер] сезона сериала [название]"
Ожидаемый формат: JSON с полями:
- Все поля как для фильма (title, plot, atmosphere, rating, facts, similar, review и т.д.)
- episode_number — номер эпизода
- season_number — номер сезона
- parent_id указывает на ID сезона
Примечание: Запросы для эпизодов и сезонов выполняются только если сериал прошёл проверку на длину (сезонов ≤ 15 и эпизодов ≤ 100). Для длинных сериалов генерация детальной разбивки пропускается.
Общие правила:
- Для всех типов заполняются одинаковые поля (как для фильма)
- Разница только в иерархии связи через parent_id
- num_of_seasons, num_episodes_per_season, status — только для сериала
- Размер plot: 100-150 слов
- Размер liked, disliked: 10-20 слов каждый
- Размер atmosphere: ~15 слов