Add interstitial endpoints and enhance media and template functionalities: implement MapInterstitialEndpoints in Program.cs, add preview functionality for media assets in MediaEndpoints, and introduce template preview capabilities in TemplateEndpoints. Remove obsolete bumper settings from Channel and related classes to streamline configuration.
build / backend (push) Successful in 5m58s
build / frontend (push) Successful in 47s
tests / backend-tests (push) Successful in 5m57s

This commit is contained in:
Leonid Pershin
2026-07-26 14:03:53 +03:00
parent 66040a8841
commit 7b12a06d1b
53 changed files with 4076 additions and 469 deletions
+144 -14
View File
@@ -132,6 +132,28 @@ const resources = {
empty: 'Коллекция пуста',
orderHint: 'Порядок частей задаётся перетаскиванием — в нём они и пойдут в эфир.',
},
interstitials: {
title: 'Ролики',
hint: 'Реклама, промо и джинглы. Перетащите ролики в сборку блока или в группу справа — блок сохранится коллекцией и пойдёт в эфир целиком.',
upload: 'Загрузить ролики',
name: 'Название',
duration: 'Длительность',
empty: 'Роликов пока нет',
noAsset: 'Без файла',
blocks: 'Блоки',
noBlocks: 'Блоков пока нет',
clipsCount: '{{count}} рол.',
blockBuilder: 'Сборка блока',
blockName: 'Название блока',
blockTotal: 'Длительность блока',
dropHint: 'Перетащите сюда ролики',
groups: 'Группы роликов',
pickGroup: 'Выберите группу',
pickGroupFirst: 'Сначала выберите группу',
dropToGroup: 'Перетащите сюда ролик или блок',
openGroup: 'Открыть группу',
newGroupName: 'Новая группа',
},
genres: {
title: 'Жанры',
hint: 'Справочник жанров: по нему собираются группы контента, в него сводятся жанры из метаданных.',
@@ -245,7 +267,7 @@ const resources = {
name: 'Название',
originalName: 'Оригинальное название (eng)',
kind: 'Тип',
kinds: { Series: 'Сериал', Single: 'Полнометражка' },
kinds: { Series: 'Сериал', Single: 'Полнометражка', Interstitial: 'Ролик' },
audience: 'Категория',
audiences: {
Kids: 'Детское',
@@ -317,6 +339,58 @@ const resources = {
maxDrift: 'Допуск, мин',
snap: 'Округление',
snapOff: 'нет',
bumperConditionsHint:
'Как часто ставить заставку и на каких переходах — условия элемента стыка, а не настройка канала.',
preview: 'Предпросмотр',
previewHide: 'Свернуть предпросмотр',
previewHint: 'Прогон по текущим правилам: ничего не пишется, курсоры слотов не двигаются.',
previewDays_one: '{{count}} сутки',
previewDays_few: '{{count}} суток',
previewDays_many: '{{count}} суток',
previewTabs: { programme: 'Программа', tape: 'Лента' },
previewKinds: {
Program: 'Программа',
Fallback: 'Фон',
SignOff: 'Конец вещания',
Ad: 'Реклама',
Promo: 'Анонс',
Bumper: 'Заставка',
},
previewLoad: 'Врезки по часам, пик — {{peak}} мин',
junctions: 'Стыки',
junctionsHint:
'Что играет между программами: реклама, анонсы, заставки. Слот может взять свой стык, иначе берётся стык по умолчанию.',
defaultJunction: 'Стык по умолчанию',
noJunction: 'без стыка',
newJunctionName: 'Новый стык',
addJunctionElement: '+ врезка',
junctionEmpty: 'пусто',
junctionFrom: 'конец',
junctionTo: 'начало',
junctionElement: 'Врезка',
junctionKind: 'Тип',
junctionKinds: {
Ad: 'Реклама',
Promo: 'Анонс',
Bumper: 'Заставка',
Filler: 'Заполнитель',
},
junctionAmountMode: 'Чем меряется',
junctionAmountModes: { Count: 'Единиц', Duration: 'Минут' },
junctionCount: 'Сколько единиц',
junctionMinutes: 'Сколько минут',
junctionAmountHint:
'В смешанной группе (ролики и готовые блоки) считайте минутами: одна «единица» там — то ли ролик, то ли блок.',
junctionRequired: 'Обязательная — не выбрасывать при нехватке времени',
junctionOnlyOnChange: 'Только при смене шоу',
junctionMinInterval: 'Не чаще, чем раз в, мин',
junctionMinIntervalHint: '0 — без ограничения.',
junctionBetween: 'Стык внутри слота',
junctionAfter: 'Стык после слота',
junctionDefault: 'по умолчанию',
bumperTemplate: 'Блок заставки',
pickBumperTemplate: 'выберите блок',
minutesShort: ' мин',
pendingChanges: 'Правила изменены — эфир идёт по старым.',
apply: 'Применить',
applied: 'Эфир пересобран, записей: {{count}}',
@@ -367,15 +441,9 @@ const resources = {
bumpersLabel: 'Заставки на переходах',
bumpersHint: 'Короткая заставка «Сейчас / Далее» между разными шоу',
bumperSelection: 'Выбор блока',
bumperSelectionRotation: 'По кругу',
bumperSelectionRandom: 'Случайно',
bumperSelectionWeighted: 'Случайно взвешенный',
bumperSelectionAlwaysFirst: 'Всегда первый',
bumperMinInterval: 'Мин. интервал, мин',
bumperShowChangeChance: 'Вероятность на смене шоу',
bumperShowChangeChanceHint: '0..1: 1 — на каждой смене, 0 — никогда',
bumperEpisodeChangeChance: 'Вероятность между сериями',
bumperEpisodeChangeChanceHint: '0..1: напр. 0.3 — примерно в 30% переходов между сериями',
bumperFont: 'Шрифт',
bumperFontSans: 'Гротеск',
bumperFontSerif: 'Антиква',
@@ -641,6 +709,28 @@ const resources = {
empty: 'Collection is empty',
orderHint: 'Drag to set the order of the parts — that is the order they air in.',
},
interstitials: {
title: 'Clips',
hint: 'Ads, promos and jingles. Drag clips into the block builder or a group on the right — a block is saved as a collection and airs as a whole.',
upload: 'Upload clips',
name: 'Name',
duration: 'Duration',
empty: 'No clips yet',
noAsset: 'No file',
blocks: 'Blocks',
noBlocks: 'No blocks yet',
clipsCount: '{{count}} clips',
blockBuilder: 'Block builder',
blockName: 'Block name',
blockTotal: 'Block duration',
dropHint: 'Drop clips here',
groups: 'Clip groups',
pickGroup: 'Pick a group',
pickGroupFirst: 'Pick a group first',
dropToGroup: 'Drop a clip or a block here',
openGroup: 'Open group',
newGroupName: 'New group',
},
genres: {
title: 'Genres',
hint: 'Genre reference: content groups are built from it, provider metadata is mapped into it.',
@@ -754,7 +844,7 @@ const resources = {
name: 'Name',
originalName: 'Original name (eng)',
kind: 'Kind',
kinds: { Series: 'Series', Single: 'Movie' },
kinds: { Series: 'Series', Single: 'Movie', Interstitial: 'Clip' },
audience: 'Category',
audiences: {
Kids: 'Kids',
@@ -826,6 +916,52 @@ const resources = {
maxDrift: 'Allowance, min',
snap: 'Snap',
snapOff: 'off',
bumperConditionsHint:
'How often a bumper is inserted and on which transitions is a junction-element condition, not a channel setting.',
preview: 'Preview',
previewHide: 'Hide preview',
previewHint: 'A run against the current rules: nothing is written, slot cursors do not move.',
previewDays_one: '{{count}} day',
previewDays_other: '{{count}} days',
previewTabs: { programme: 'Programme', tape: 'Tape' },
previewKinds: {
Program: 'Programme',
Fallback: 'Background',
SignOff: 'Sign-off',
Ad: 'Ad',
Promo: 'Promo',
Bumper: 'Bumper',
},
previewLoad: 'Breaks per hour, peak — {{peak}} min',
junctions: 'Junctions',
junctionsHint:
'What plays between programmes: ads, promos, bumpers. A slot may pick its own junction, otherwise the default one is used.',
defaultJunction: 'Default junction',
noJunction: 'no junction',
newJunctionName: 'New junction',
addJunctionElement: '+ break',
junctionEmpty: 'empty',
junctionFrom: 'end',
junctionTo: 'start',
junctionElement: 'Break',
junctionKind: 'Kind',
junctionKinds: { Ad: 'Ad', Promo: 'Promo', Bumper: 'Bumper', Filler: 'Filler' },
junctionAmountMode: 'Measured in',
junctionAmountModes: { Count: 'Units', Duration: 'Minutes' },
junctionCount: 'How many units',
junctionMinutes: 'How many minutes',
junctionAmountHint:
'For a mixed group (clips and ready-made blocks) count in minutes: one "unit" there is either a clip or a whole block.',
junctionRequired: 'Required — never dropped when time runs short',
junctionOnlyOnChange: 'Only when the show changes',
junctionMinInterval: 'No more often than once per, min',
junctionMinIntervalHint: '0 — no limit.',
junctionBetween: 'Junction inside the slot',
junctionAfter: 'Junction after the slot',
junctionDefault: 'default',
bumperTemplate: 'Bumper block',
pickBumperTemplate: 'pick a block',
minutesShort: ' min',
pendingChanges: 'Rules changed — the air still follows the old ones.',
apply: 'Apply',
applied: 'Air rebuilt, entries: {{count}}',
@@ -876,15 +1012,9 @@ const resources = {
bumpersLabel: 'Transition bumpers',
bumpersHint: 'Short “Now / Next” bumper between different shows',
bumperSelection: 'Block selection',
bumperSelectionRotation: 'Rotation',
bumperSelectionRandom: 'Random',
bumperSelectionWeighted: 'Weighted random',
bumperSelectionAlwaysFirst: 'Always first',
bumperMinInterval: 'Min interval, min',
bumperShowChangeChance: 'Chance on show change',
bumperShowChangeChanceHint: '0..1: 1 — every change, 0 — never',
bumperEpisodeChangeChance: 'Chance between episodes',
bumperEpisodeChangeChanceHint: '0..1: e.g. 0.3 — about 30% of same-show transitions',
bumperFont: 'Font',
bumperFontSans: 'Sans',
bumperFontSerif: 'Serif',