Refactor ShowAudience handling across the application to align with MPAA rating system. Update ShowAudience enum to reflect new ratings (G, PG, PG-13, R, NC-17) and adjust related data models, API types, and frontend components to support nullable audience values. Enhance metadata handling to incorporate content ratings from external sources, ensuring proper audience filtering in scheduling logic. Update documentation to clarify changes in audience categorization and its implications for content management.
ci / build-backend (push) Successful in 1m28s
ci / build-frontend (push) Successful in 51s
ci / tests (push) Successful in 2m7s
ci / sonar (push) Successful in 4m31s

This commit is contained in:
Leonid Pershin
2026-07-26 22:18:14 +03:00
parent c4c93cff02
commit fdb0f321e4
34 changed files with 1868 additions and 116 deletions
+19 -13
View File
@@ -28,7 +28,7 @@
канал создаётся с нуля, но так, чтобы это было приятно.
Возрастных ограничений на стороне зрителя нет. Что показывает канал — то и смотришь, как и было.
Возрастная категория — инструмент планирования (не поставить взрослое в детский эфир), а не гейт.
Возрастной рейтинг — инструмент планирования (не поставить взрослое в детский эфир), а не гейт.
### 1.1. От чего уходим
@@ -128,19 +128,25 @@ ShowGenre — многие-ко-многим
Отдельных свободных тегов не заводим: их роль («ретро», «новогоднее») выполняют группы с ручным
составом, а это одна сущность вместо двух.
**Возрастная категория.** `ShowAudience` расширяется до пяти значений, **упорядоченных по возрастанию
строгости** — правила формулируются как «до 23:00 не строже подросткового», поэтому порядок в enum
значим:
**Возрастной рейтинг.** `ShowAudience` — шкала MPAA, **упорядоченная по возрастанию строгости**:
правила формулируются как «до 23:00 не строже PG-13», поэтому порядок в enum значим.
```
Kidsдетское
Family — семейное
Teen — подростковое
General — общее
Adult — взрослое
G без ограничений
PG — с родителями
PG-13 — не рекомендуется до 13
R — до 17 со взрослым
NC-17 — только взрослым
```
Текущие три значения (`General=0, Kids=1, Adult=2`) не отсортированы и переписываются.
Шкала взята не абстрактная, а ровно та, которой оперируют внешние источники (`Rated` у OMDb,
сертификации у TMDb): рейтинг из метаданных ложится в поле без промежуточной классификации,
которую пришлось бы придумывать и объяснять.
**Отсутствие рейтинга — `null`, а не значение шкалы.** «Не проставлен» и «подходит всем» — разные
факты; в одной ячейке теряются оба. Контент без рейтинга планировщик **не отсекает**: источники
проставляют рейтинг далеко не всему (на неамериканском кино чаще всего не проставляют вовсе),
и трактовка «неизвестное значит взрослое» вымела бы из эфира большую часть библиотеки.
**Коллекция** — франшиза: упорядоченный набор шоу. «Терминатор» — это три `Show(Single)` в порядке
1→2→3. Живёт в библиотеке рядом с шоу, потому что «одно произведение из трёх частей» — факт о контенте,
@@ -209,7 +215,7 @@ GroupItem
"elementKinds": ["show", "collection"],
"showKinds": ["series", "single"],
"genres": { "anyOf": ["боевик", "триллер"] },
"audience": { "max": "Teen" },
"audience": { "max": "PG-13" },
"year": { "min": 1989, "max": 1999 },
"unitDurationMinutes": { "min": 20, "max": 25 }
}
@@ -476,7 +482,7 @@ ffmpeg с кэшем по паре шоу, `ScheduleBumperResolver` — вся
| Правило | Параметры | Смысл |
|---------|-----------|-------|
| `maxAudienceByTime` | `{from, to, maxAudience}` | Детское время: до 23:00 не строже подросткового |
| `maxAudienceByTime` | `{from, to, maxAudience}` | Детское время: до 23:00 не строже `PG-13` |
| `cooldown` | `{days}` | Не показывать элемент, игравший последние N дней |
| `maxRepeatsInWindow` | `{windowDays, max}` | Не чаще N раз за период |
@@ -654,7 +660,7 @@ seed = hash(channelId, date, slotId, occurrenceInDay)
| Дыра в сетке | интервал не покрыт даже фоном | «Не покрыто: вт 03:00–06:00» |
| Пересечение слотов | overlap внутри одного слоя | «Слоты пересекаются» |
| Недостижимый кулдаун | `cooldownDays × выходов в день > itemCount` | «Остывание 28 дней невыполнимо при 3 позициях» |
| Возрастной конфликт | группа содержит строже, чем позволяет окно | «В группе есть 18+, слот стоит в детском времени» |
| Возрастной конфликт | группа содержит строже, чем позволяет окно | «В группе есть `NC-17`, слот стоит в детском времени» |
Запас группы показывается прямо в инспекторе слота: «342 позиции · 118 ч · полный цикл 6.2 недели».