Update .env.example to extend scheduler configuration: increase HorizonDays to 7 and change RetentionHours to RetentionDays with a default of 90. Revise documentation in media-storage-and-streaming.md to reflect these changes. Remove outdated tv-scheduler-tasks.md file.
This commit is contained in:
@@ -119,9 +119,10 @@
|
||||
достраивается заново от её конца. Обычный фоновый тик только **расширяет** горизонт (без удаления).
|
||||
Override, начинающийся в будущем, подхватится и штатным тиком.
|
||||
|
||||
**Ретеншн.** Прошедшие `ScheduleEntry` старше `Scheduler__RetentionHours` можно удалять (оставляя
|
||||
небольшое окно назад для EPG «что только что было»). Сегменты ассетов при этом не трогаются —
|
||||
ассеты переиспользуются, чистится только расписание.
|
||||
**Ретеншн.** Прошедшие `ScheduleEntry` старше `Scheduler__RetentionDays` удаляются. Окно длинное
|
||||
(90 суток по умолчанию), потому что история показов берётся из самой ленты: по ней работают остывание
|
||||
и потолок повторов, и окно обязано покрывать самое долгое правило канала. Сегменты ассетов при этом
|
||||
не трогаются — ассеты переиспользуются, чистится только расписание.
|
||||
|
||||
**Дыры.** В норме их нет. Если у канала нет ни одного включённого шоу с готовыми сериями (или
|
||||
планировщик отстал) — эфир играет `FillerAssetId` по кругу, пока не появится расписание.
|
||||
@@ -229,8 +230,8 @@ Storage__MinFreeSpaceBytes=10737418240
|
||||
Media__FfmpegPath=/usr/bin/ffmpeg
|
||||
Media__FfprobePath=/usr/bin/ffprobe
|
||||
Media__MaxUploadBytes=21474836480
|
||||
Scheduler__HorizonDays=3
|
||||
Scheduler__RetentionHours=24
|
||||
Scheduler__HorizonDays=7
|
||||
Scheduler__RetentionDays=90
|
||||
Scheduler__TickMinutes=30
|
||||
```
|
||||
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
# Планировщик эфира: задачи
|
||||
|
||||
Разбивка [tv-scheduler-architecture.md](tv-scheduler-architecture.md) на задачи для разработки.
|
||||
Ссылки вида «см. 3.4» — на разделы спеки.
|
||||
|
||||
Задачи внутри среза идут в порядке зависимостей: следующая опирается на предыдущие. Срезы —
|
||||
последовательно, каждый заканчивается работающим каналом.
|
||||
|
||||
## Статус
|
||||
|
||||
`[x]` сделано · `[~]` частично (что осталось — под задачей) · `[ ]` не начато.
|
||||
|
||||
**Срез 1 закрыт целиком.** Канал вещает по сетке: библиотека с жанрами и коллекциями, группы,
|
||||
шаблон со слоями и слотами, генератор с трейсом, применение по кнопке, UI сетки. Старая ротация снесена.
|
||||
|
||||
**Срез 2 закрыт целиком.** Ролики со своим экраном и сборкой блоков, шаблоны стыков с редактором
|
||||
цепочки, привязка стыков к слотам, предпросмотр без записи. Мёртвые настройки заставок
|
||||
(`BumperMinIntervalMinutes`, обе `*Chance`, `NextBumperIndex`) удалены — условия показа живут
|
||||
в элементе стыка; из `BumperSelection` убрана «ротация», у которой не было реализации.
|
||||
|
||||
**Срез 3 закрыт целиком.** Применимость слоёв правится из UI, слои переупорядочиваются
|
||||
перетаскиванием и выключаются, сетку можно посмотреть на конкретную дату, слоты двигаются
|
||||
и растягиваются мышью, день копируется на другие дни. Добавлены жёсткие фильтры кандидатов —
|
||||
детское время и потолок повторов.
|
||||
|
||||
**Срез 4 закрыт целиком.** Проверки по правилам показываются прямо в редакторе, пост-проверки
|
||||
дают предупреждения по готовой ленте, у предпросмотра появилась вкладка «Проблемы» с тепловой
|
||||
картой повторов, у каждой записи — трейс «почему это здесь», применение идёт через диф
|
||||
с подсветкой ближайших суток, сетка копируется на другой канал.
|
||||
|
||||
**Смежная зрительская часть закрыта.** Номера каналов с сортировкой публичного списка,
|
||||
переключение по номерам стрелками (глобальный флаг рядом с флагом регистрации), логотип-оверлей,
|
||||
часы, плашка «Далее» и аналоговый фильтр. Всё опционально и по умолчанию выключено.
|
||||
|
||||
**Проверено:** 142 доменных, 72 прикладных и 11 интеграционных тестов. Интеграционные поднимают
|
||||
настоящий Postgres в контейнере, применяют к нему все миграции и гоняют конвейер генерации,
|
||||
копирование шаблона и проверки сетки — то есть миграции и слой «Application ↔ EF» работают.
|
||||
|
||||
**Не проверено:** приложение ни разу не поднималось целиком, ни один экран не открывался в браузере,
|
||||
на реальной базе с реальным контентом ничего не запускалось.
|
||||
|
||||
---
|
||||
|
||||
## Общие требования к любой задаче
|
||||
|
||||
Выполняется для каждой задачи, ниже в тексте не повторяется:
|
||||
|
||||
- **Границы слоёв.** `Api → Infrastructure → Application → Domain`. В `Application` — только порты,
|
||||
никаких `Npgsql`/`AspNetCore.Identity`.
|
||||
- **Миграция.** Изменение модели → `dotnet ef migrations add <Name> --project src/TeleWave.Infrastructure --startup-project src/TeleWave.Api`.
|
||||
- **Ошибки** — через `Result<T>` и каталоги `XErrors`, не исключениями. Валидация — FluentValidation.
|
||||
- **Тесты.** Доменная логика — `tests/TeleWave.Domain.Tests`, хендлеры — `tests/TeleWave.Application.Tests`,
|
||||
эндпоинты — `tests/TeleWave.Integration.Tests`. Сборка не должна давать предупреждений
|
||||
(`TreatWarningsAsErrors=true`).
|
||||
- **Фронт.** Типы и клиент — в `api.ts` соответствующей фичи. Все строки — в `frontend/src/shared/lib/i18n.ts`,
|
||||
**обе локали** (`ru` и `en`). Проверка: `pnpm lint && pnpm typecheck`.
|
||||
- **Изображения** — только через реестр `Image` и контрол `ImageGallery`, своих файловых полей не заводить.
|
||||
|
||||
---
|
||||
|
||||
## Срез 1 — сетка вещает
|
||||
|
||||
Цель: канал вещает по слотам вместо взвешенной ротации. Реклама и заставки временно работают как
|
||||
сейчас, стыков ещё нет.
|
||||
|
||||
### 1A. Библиотека
|
||||
|
||||
#### [x] 1.1. Справочник жанров
|
||||
`Domain` `Infrastructure` `Application` `Api` `Frontend`
|
||||
|
||||
Сущность `Genre` (id, name, externalIds) и связь `ShowGenre` (showId, genreId, isPrimary).
|
||||
Идемпотентный сид жанров TMDb в `DbInitializer`. CRUD жанров в админке (список, создание,
|
||||
переименование, удаление с проверкой на использование).
|
||||
|
||||
Готово: жанры есть в БД после старта, админ может править справочник.
|
||||
|
||||
#### [x] 1.2. Жанры на шоу
|
||||
`Application` `Api` `Frontend`
|
||||
|
||||
Проставление жанров шоу вручную (мультиселект + выбор основного). Автоматический маппинг жанров
|
||||
провайдера при `ApplyShowMetadata` / `UpdateShowMetadata` по `Genre.externalIds`; нераспознанные
|
||||
жанры логируются, шоу не ломают. Фильтр по жанру в `ListShows`.
|
||||
|
||||
Готово: у шоу видны и правятся жанры, TMDb-метаданные проставляют их сами, список шоу фильтруется.
|
||||
|
||||
#### [x] 1.3. Расширение `ShowAudience`
|
||||
`Domain` `Infrastructure` `Frontend`
|
||||
|
||||
Пять значений по возрастанию строгости: `Kids → Family → Teen → General → Adult` (см. 3.1).
|
||||
Текущий enum не отсортирован и переписывается; миграция переносит существующие значения
|
||||
один-в-один. Обновить селекты и отображение в UI шоу.
|
||||
|
||||
Готово: порядок значений в enum монотонен по строгости, старые данные не потеряны.
|
||||
|
||||
#### [x] 1.4. Коллекции
|
||||
`Domain` `Infrastructure` `Application` `Api` `Frontend`
|
||||
|
||||
`Collection` (id, name, posterImageId) + `CollectionItem` (collectionId, showId, position).
|
||||
CRUD, изменение порядка перетаскиванием, постер через `ImageGallery`. На экране шоу — блок
|
||||
«входит в коллекции».
|
||||
|
||||
Готово: франшизу из трёх фильмов можно собрать, переупорядочить и открыть с экрана шоу.
|
||||
|
||||
### 1B. Группы
|
||||
|
||||
#### [x] 1.5. Модель группы
|
||||
`Domain` `Infrastructure`
|
||||
|
||||
`Group` (id, name, description, filter jsonb, cachedStats jsonb) + `GroupItem` (groupId,
|
||||
elementKind, elementId, weight, position). См. 3.3. Группы глобальные, без привязки к каналу.
|
||||
|
||||
Готово: модель и миграция.
|
||||
|
||||
#### [x] 1.6. CRUD групп и состава
|
||||
`Application` `Api`
|
||||
|
||||
Создание/переименование/удаление группы (с проверкой, что она не используется слотами), добавление
|
||||
и удаление позиций, изменение порядка и весов. Удаление шоу или коллекции из библиотеки чистит
|
||||
позиции групп.
|
||||
|
||||
Готово: состав группы правится через API, висячих ссылок не остаётся.
|
||||
|
||||
#### [x] 1.7. Фильтр набора и статистика
|
||||
`Application` `Api`
|
||||
|
||||
Выполнение `filter` (см. 3.3) как **разового набора**: возвращает подходящие позиции, отдельная
|
||||
команда добавляет их в состав. Пересчёт `cachedStats` (число позиций, число единиц, суммарная
|
||||
длительность) при изменении состава и фоново при изменении библиотеки.
|
||||
|
||||
Готово: «добавить найденное по фильтру» работает, в API группы отдаётся актуальная статистика.
|
||||
|
||||
#### [x] 1.8. Редактор группы
|
||||
`Frontend`
|
||||
|
||||
Двухпанельный экран (см. 6.2): слева конструктор фильтра, справа состав с drag & drop порядка.
|
||||
Перетаскивание шоу и коллекций из библиотеки. Вес — колонка за «дополнительно». Бейдж «доступно
|
||||
N новых позиций по фильтру».
|
||||
|
||||
Готово: группу можно собрать целиком мышкой, статистика видна на экране.
|
||||
|
||||
### 1C. Шаблон, слои, слоты
|
||||
|
||||
#### [x] 1.9. Модель шаблона
|
||||
`Domain` `Infrastructure`
|
||||
|
||||
`ScheduleTemplate` (channelId, name, defaultJunctionId, fallbackGroupId, revision) → `GridLayer`
|
||||
(templateId, name, priority, applicability jsonb, isEnabled) → `Slot` (все поля из 3.4, включая
|
||||
`slotKind`, `snapToMinutes`, `overflowPolicy`, `isAnchor`). `SlotState` (см. 3.6).
|
||||
|
||||
В этом срезе используются только базовый слой и фоновый; применимость слоёв — в срезе 3.
|
||||
|
||||
Готово: модель и миграция; у канала при создании появляется шаблон с фоновым слоем.
|
||||
|
||||
#### [x] 1.10. Изменения в `Channel`
|
||||
`Domain` `Infrastructure` `Application` `Api`
|
||||
|
||||
Добавить `utcOffsetMinutes` (дефолт 180), `dayStartTime` (дефолт 06:00), `templateId`, `number`.
|
||||
Обновить создание канала и команду настроек.
|
||||
|
||||
Готово: канал создаётся с шаблоном, оффсетом и началом вещательных суток.
|
||||
|
||||
#### [x] 1.11. CRUD слоёв и слотов
|
||||
`Application` `Api`
|
||||
|
||||
Создание/правка/удаление слотов, проверка пересечений внутри слоя, валидация кратности времён,
|
||||
проверка что группа существует и не пуста. Пометка шаблона изменённым (`revision`) при любой правке.
|
||||
|
||||
Готово: сетку можно собрать через API, некорректный слот не сохраняется.
|
||||
|
||||
### 1D. Генератор
|
||||
|
||||
#### [x] 1.12. Разрешение сетки
|
||||
`Domain`
|
||||
|
||||
Чистая функция: на момент времени вернуть активный слот — применимые слои по убыванию приоритета,
|
||||
первый слот, накрывающий момент. Учёт вещательных суток (`dayStartTime`) при определении дня недели.
|
||||
Юнит-тесты на границы суток и перекрытие слоёв.
|
||||
|
||||
Готово: `tests/TeleWave.Domain.Tests` покрывает выбор слота, включая ночь после полуночи.
|
||||
|
||||
#### [x] 1.13. Стратегии выбора
|
||||
`Domain`
|
||||
|
||||
`sequential`, `randomWithCooldown`, `fixed` (см. 3.5). Внутри элемента единицы всегда по порядку
|
||||
от курсора. Разворачивание элемента в последовательность единиц: сериал → серии, коллекция →
|
||||
фильмы (сериал внутри коллекции разворачивается), одиночное шоу → одна единица (см. 3.2).
|
||||
|
||||
Готово: юнит-тесты на каждую стратегию, включая заворот в конце и пустую группу.
|
||||
|
||||
#### [x] 1.14. Наполнение слота
|
||||
`Domain`
|
||||
|
||||
Бюджет по `blockMode` (`count` / `duration` / `fillSlot`), `overflowPolicy`, продвижение `SlotState`.
|
||||
Пока без стыков и якорей — контент встык.
|
||||
|
||||
Готово: юнит-тесты на все три режима блока и три политики переполнения.
|
||||
|
||||
#### [x] 1.15. Фоновый слой и филлер
|
||||
`Domain`
|
||||
|
||||
Незакрытые интервалы отдаются слоту фонового слоя; если пуст и он — `fallbackGroupId` шаблона;
|
||||
если пуст и он — зацикленный аварийный ассет канала. Все длительности округляются вверх до сегмента
|
||||
(см. 2.2).
|
||||
|
||||
Готово: дыр в ленте не бывает; юнит-тест на канал с пустой группой.
|
||||
|
||||
#### [x] 1.16. Оркестратор генерации
|
||||
`Application`
|
||||
|
||||
Новый генератор по образцу `ScheduleGenerator`: загрузка конфигурации, вызов чистой части,
|
||||
материализация с записью `trace` (см. 3.9), продвижение `SlotState`, advisory-lock канала,
|
||||
транзакция. Горизонт из `SchedulerOptions.HorizonDays` (поднять дефолт до 7).
|
||||
|
||||
Готово: команда генерации наполняет расписание канала на неделю вперёд.
|
||||
|
||||
#### [x] 1.17. Ретеншн и обслуживание
|
||||
`Application` `Infrastructure`
|
||||
|
||||
`SchedulerOptions.RetentionHours` → `RetentionDays` (дефолт 90). Фоновая задача обслуживания
|
||||
(см. 3.6): чистка записей сверх ретеншна, осиротевших `BumperAsset`, временных файлов.
|
||||
Валидация: правило требует истории глубже, чем хранится → предупреждение.
|
||||
|
||||
Готово: старые записи и неиспользуемые заставки удаляются по расписанию.
|
||||
|
||||
#### [x] 1.18. Применение по кнопке
|
||||
`Application` `Api`
|
||||
|
||||
Правка правил не трогает эфир, только помечает шаблон изменённым. Отдельная команда «применить»
|
||||
пересобирает хвост от `now` (см. 4.5). В ответ — сколько записей затронуто.
|
||||
|
||||
Готово: сохранение слота эфир не двигает; применение пересобирает будущее и идемпотентно.
|
||||
|
||||
#### [x] 1.19. Золотые тесты генератора
|
||||
`Domain.Tests`
|
||||
|
||||
Набор эталонных конфигураций (канал с одним слоем, с фоном, с коллекцией, с пустой группой) и
|
||||
зафиксированные сутки эфира. Сравнение посуточной ленты целиком, обновление эталона — явной
|
||||
командой.
|
||||
|
||||
Готово: правка генератора показывает построчный диф вместо «тесты упали».
|
||||
|
||||
### 1E. Снос старого
|
||||
|
||||
#### [x] 1.20. Удаление ротации
|
||||
`Domain` `Infrastructure` `Application` `Api` `Frontend`
|
||||
|
||||
Удалить `ChannelShow`, `ChannelShowHour`, `ChannelAd`, `ProgrammingOverride`, `OverrideShow`,
|
||||
`AdInsertion`, `OverrideMode`, `OverrideRecurrence`, `SchedulePlanner` и все `Planner*`-модели,
|
||||
соответствующие команды и эндпоинты, компоненты `AddShowForm`, `AddAdForm`, `ChannelShowRow`,
|
||||
`OverrideForm` (см. 9).
|
||||
|
||||
Реклама и заставки на этом шаге временно генерируются старым способом — переезжают в стыки в срезе 2.
|
||||
|
||||
Готово: старый планировщик удалён, сборка и тесты зелёные.
|
||||
|
||||
### 1F. UI
|
||||
|
||||
#### [x] 1.21. Read-only сетка и инспектор слота
|
||||
`Frontend`
|
||||
|
||||
Недельная сетка (ось X — дни, ось Y — время от `dayStartTime`), пока без перетаскивания: добавление
|
||||
слота кнопкой на пустом месте, правка в инспекторе (см. 6.1). Индикатор «правила изменены» с кнопкой
|
||||
применения. Экран `ChannelDetail` переписывается.
|
||||
|
||||
Готово: канал настраивается и вещает по сетке целиком через новый UI.
|
||||
|
||||
---
|
||||
|
||||
## Срез 2 — врезки
|
||||
|
||||
#### [x] 2.1. Ролики как `Show(Kind = Interstitial)`
|
||||
`Domain` `Infrastructure` `Application`
|
||||
|
||||
Новое значение `ShowKind`. Исключение таких шоу из обычного списка библиотеки и из запросов
|
||||
метаданных.
|
||||
|
||||
#### [x] 2.2. Раздел «Ролики»
|
||||
`Api` `Frontend`
|
||||
|
||||
Отдельный экран (см. 6.7): плоский список с длительностями и превью, массовая загрузка, сборка
|
||||
блока перетаскиванием с показом суммарной длительности (сохраняется как коллекция), группы роликов.
|
||||
|
||||
#### [x] 2.3. Модель шаблона стыка
|
||||
`Domain` `Infrastructure`
|
||||
|
||||
`JunctionTemplate` + `JunctionElement` (см. 3.7), включая `amountMode` (`count` / `duration`) и
|
||||
структурированные `conditions`. Ссылки `junctionBetweenId` / `junctionAfterId` на слоте.
|
||||
|
||||
#### [x] 2.4. Раскладка врезок
|
||||
`Domain`
|
||||
|
||||
Наполнение стыка: обязательные элементы, набор по бюджету, проверка условий, округление до сегмента.
|
||||
Коллекция входит целиком и не разрезается. Юнит-тесты на смешанную группу блоков и одиночных роликов.
|
||||
|
||||
#### [x] 2.5. Переезд заставок в стык
|
||||
`Application`
|
||||
|
||||
`BumperTemplate` / `ScheduleBumperResolver` и рендер не меняются — меняется точка вызова: вместо
|
||||
настроек канала (`BumperSelection`, `NextBumperIndex`, `BumperMinIntervalMinutes`, обе `*Chance`)
|
||||
условия берутся из элемента стыка. Перечисленные поля канала удаляются, `BumperFont` остаётся.
|
||||
|
||||
#### [x] 2.6. Якоря и округление стартов
|
||||
`Domain`
|
||||
|
||||
Жёсткие старты якорных слотов (не начинать единицу, перелезающую через якорь), мягкое округление
|
||||
`snapToMinutes` с отказом при превышении `maxDriftMinutes` (см. 4.3). Юнит-тесты на оба механизма
|
||||
и на их сочетание.
|
||||
|
||||
#### [x] 2.7. Редактор стыка
|
||||
`Frontend`
|
||||
|
||||
Горизонтальная цепочка с перетаскиванием элементов, линейка суммарной длительности, попап параметров
|
||||
(см. 6.3).
|
||||
|
||||
#### [x] 2.8. Предпросмотр
|
||||
`Application` `Api` `Frontend`
|
||||
|
||||
Прогон генератора на черновике правил **без записи и без продвижения курсоров**. Заставки —
|
||||
плейсхолдерами известной длины, реальный рендер только при применении (см. 3.7). Вкладки «Программа»
|
||||
и «Лента» с гистограммой нагрузки врезок по часам.
|
||||
|
||||
---
|
||||
|
||||
## Срез 3 — гибкость и календарь
|
||||
|
||||
#### [x] 3.1. Применимость слоёв
|
||||
`Domain` `Infrastructure` `Application`
|
||||
|
||||
|
||||
`applicability`: дни недели, разовые диапазоны дат, ежегодные диапазоны, конкретные даты (см. 3.4).
|
||||
Приоритеты, разрешение конфликтов. Юнит-тесты на пересекающиеся слои и на границу года в ежегодном
|
||||
диапазоне.
|
||||
|
||||
#### [x] 3.2. Фильтры кандидатов
|
||||
`Domain`
|
||||
|
||||
|
||||
`maxAudienceByTime`, `cooldown`, `maxRepeatsInWindow` (см. 3.8) — применяются **до** взвешенного
|
||||
выбора. История берётся из материализованной ленты. `fallback` стратегии, когда остывание отсекло
|
||||
всех.
|
||||
|
||||
#### [x] 3.3. Слот `repeat`
|
||||
`Domain` `Application`
|
||||
|
||||
Чтение уже записанной ленты по `repeatSource` (daysAgo, time, durationMinutes) и перенос найденных
|
||||
программ. Источник пуст → фон + предупреждение.
|
||||
|
||||
#### [x] 3.4. Слот `signOff`
|
||||
`Domain` `Application` `Frontend`
|
||||
|
||||
Конец вещания: заполнение зацикленным ассетом, пометка в EPG «эфир не ведётся», отображение
|
||||
в программе.
|
||||
|
||||
#### [x] 3.5. Панель слоёв
|
||||
`Frontend`
|
||||
|
||||
|
||||
Список слоёв с видимостью и приоритетом, drag для переупорядочивания, выбор редактируемого слоя,
|
||||
штриховка перекрытых слотов, переключатель даты («показать сетку на 25 декабря»).
|
||||
|
||||
#### [x] 3.6. Drag & drop в календаре
|
||||
`Frontend`
|
||||
|
||||
Перетаскивание слотов и изменение длительности за края, копирование дня на другие дни, копирование
|
||||
недели. Своя реализация — готовые календари не покрывают слои, вещательные сутки и якоря (см. 6.1).
|
||||
|
||||
---
|
||||
|
||||
## Срез 4 — эксплуатация
|
||||
|
||||
#### [x] 4.1. Валидация до генерации
|
||||
`Application` `Api` `Frontend`
|
||||
|
||||
Проверки из 5.1: нехватка контента, пустая группа, дыра в сетке, пересечение слотов, недостижимый
|
||||
кулдаун, возрастной конфликт. Отдаются вместе с шаблоном, показываются в редакторе.
|
||||
|
||||
#### [x] 4.2. Пост-проверки
|
||||
`Application`
|
||||
|
||||
Потолок врезок в час, доля жанра за сутки, превышение дрейфа, доля эфира у фона (см. 3.8, 5.2).
|
||||
Предупреждения, не ошибки — ничего не переигрывается.
|
||||
|
||||
#### [x] 4.3. Вкладка «Проблемы» и тепловая карта
|
||||
`Frontend`
|
||||
|
||||
Сгруппированные предупреждения с переходом к источнику; матрица «элемент × день» с яркостью
|
||||
по числу показов.
|
||||
|
||||
#### [x] 4.4. «Почему это здесь»
|
||||
`Api` `Frontend`
|
||||
|
||||
Отдача `trace` записи и экран цепочки происхождения (см. 6.5).
|
||||
|
||||
#### [x] 4.5. Диф перед применением
|
||||
`Application` `Api` `Frontend`
|
||||
|
||||
Сравнение текущего хвоста с пересчитанным, список изменений, отдельная подсветка ближайших суток
|
||||
(см. 6.6).
|
||||
|
||||
#### [x] 4.6. Копирование шаблона
|
||||
`Application` `Api` `Frontend`
|
||||
|
||||
Глубокая копия шаблона, слоёв и слотов на другой канал; группы не копируются, они общие.
|
||||
|
||||
---
|
||||
|
||||
## Смежное — зрительская часть
|
||||
|
||||
От планировщика не зависит, делается параллельно в любой момент. Всё опционально и по умолчанию
|
||||
выключено (см. 6.8).
|
||||
|
||||
#### [x] V.1. Номер канала
|
||||
`Domain` `Application` `Api` `Frontend`
|
||||
|
||||
|
||||
`Channel.number`, уникальность, сортировка публичного списка по номеру.
|
||||
|
||||
#### [x] V.2. Переключение по номерам
|
||||
`Frontend`
|
||||
|
||||
Вверх-вниз по номерам, короткий чёрный кадр, номер в углу на секунду. Включается глобальным флагом
|
||||
в `AppSetting` рядом с флагом регистрации. Сетка каналов остаётся вторым способом навигации.
|
||||
|
||||
#### [x] V.3. Логотип канала
|
||||
`Domain` `Api` `Frontend`
|
||||
|
||||
`logoImageId` (реестр изображений), угол и прозрачность. Оверлей поверх `<video>`, без касания
|
||||
ffmpeg.
|
||||
|
||||
#### [x] V.4. Часы и плашка «Далее»
|
||||
`Frontend`
|
||||
|
||||
Часы — опция канала. Плашка в конце программы по данным EPG.
|
||||
|
||||
#### [x] V.5. Аналоговый фильтр
|
||||
`Domain` `Frontend`
|
||||
|
||||
`analogFilterStrength` на канале, CSS-фильтр или шейдер, по умолчанию выключен.
|
||||
|
||||
---
|
||||
|
||||
## Порядок и параллельность
|
||||
|
||||
- **1A** (библиотека) и **1B** (группы) можно вести параллельно; **1C** зависит от 1B.
|
||||
- **1D** (генератор) зависит от 1C, но чистые части (1.12–1.15) пишутся и тестируются без БД.
|
||||
- **1E** (снос) выполняется только после 1.16 — иначе канал остаётся без эфира.
|
||||
- **Срез 2** целиком зависит от среза 1; внутри него 2.1–2.2 (ролики) независимы от 2.3–2.7 (стыки).
|
||||
- **Смежное** не зависит ни от чего, кроме V.2, которому нужен V.1.
|
||||
Reference in New Issue
Block a user