Enhance channel management: add support for preferred weight multipliers and preferred hours in channel shows, update related data models and API endpoints, and implement validation for new properties. Refactor scheduling logic to utilize preferred hours for weight adjustments during show planning.

This commit is contained in:
Leonid Pershin
2026-07-25 18:44:28 +03:00
parent 358adbff21
commit 8718e8b6bf
36 changed files with 2921 additions and 97 deletions
+16 -1
View File
@@ -127,7 +127,7 @@ export type AdInsertion = 'BetweenBlocks' | 'BetweenEpisodes'
export type OverrideMode = 'Exclusive' | 'Boost'
export type ScheduleEntryKind = 'Program' | 'Ad' | 'Bumper'
export type BumperFont = 'Sans' | 'Serif'
export type BumperSelection = 'Rotation' | 'Random' | 'AlwaysFirst'
export type BumperSelection = 'Rotation' | 'Random' | 'AlwaysFirst' | 'WeightedRandom'
export type BumperTextKind = 'NowNext' | 'Free'
export type BumperTrigger = 'OnShowChange' | 'BetweenEpisodes' | 'Both'
@@ -136,6 +136,10 @@ export type BumperSettings = {
font: BumperFont
minIntervalMinutes: number
selection: BumperSelection
/** Вероятность заставки на смене шоу (0..1). */
showChangeChance: number
/** Вероятность заставки между блоками одного шоу (0..1). */
episodeChangeChance: number
}
/** Подблок (текст-вариант): свой текст + правило показа поверх стиля/звука блока. */
@@ -149,6 +153,8 @@ export type BumperTextVariantDto = {
line1: string
line2: string
trigger: BumperTrigger
/** Вес при стратегии выбора «случайно взвешенный» (0 — не выбирается). */
weight: number
}
export type BumperTemplateDto = {
@@ -173,6 +179,9 @@ export type ChannelSummaryDto = {
isEnabled: boolean
}
/** Окно предпочтительных часов [startHour, endHour) суток (UTC). */
export type HourWindow = { startHour: number; endHour: number }
export type ChannelShowDto = {
id: string
showId: string
@@ -182,6 +191,9 @@ export type ChannelShowDto = {
blockValue: number
isEnabled: boolean
nextEpisodeIndex: number
/** Во сколько раз усиливать вес в предпочтительные часы (1 — без буста). */
preferredWeightMultiplier: number
preferredHours: HourWindow[]
}
export type ChannelAdDto = {
@@ -227,6 +239,9 @@ export type ScheduleEntryDto = {
showName: string | null
episodeIndex: number | null
seasonEpisode: string | null
/** Для заставок: имя подблока и его текст — для метки в расписании. */
bumperName: string | null
bumperText: string | null
}
// ── Публичный эфир ─────────────────────────────────────────────────────────
+28
View File
@@ -203,8 +203,13 @@ const resources = {
bumperSelection: 'Выбор блока',
bumperSelectionRotation: 'По кругу',
bumperSelectionRandom: 'Случайно',
bumperSelectionWeighted: 'Случайно взвешенный',
bumperSelectionAlwaysFirst: 'Всегда первый',
bumperMinInterval: 'Мин. интервал, мин',
bumperShowChangeChance: 'Вероятность на смене шоу',
bumperShowChangeChanceHint: '0..1: 1 — на каждой смене, 0 — никогда',
bumperEpisodeChangeChance: 'Вероятность между сериями',
bumperEpisodeChangeChanceHint: '0..1: напр. 0.3 — примерно в 30% переходов между сериями',
bumperFont: 'Шрифт',
bumperFontSans: 'Гротеск',
bumperFontSerif: 'Антиква',
@@ -234,6 +239,8 @@ const resources = {
bumperTriggerOnShowChange: 'При смене шоу',
bumperTriggerBetweenEpisodes: 'Между сериями',
bumperTriggerBoth: 'Оба',
bumperVariantWeight: 'Вес',
bumperVariantWeightHint: 'Для стратегии «случайно взвешенный»: чем больше — тем чаще (0 — не выбирается)',
bumperDefault: 'по умолчанию',
bumperSeconds: 'с',
bumperDefaultDuration: '≈8 с (джингл)',
@@ -262,6 +269,13 @@ const resources = {
blockDuration: 'По времени',
episodes: 'серий',
minutes: 'минут',
preferredHours: 'Часы',
preferredMultiplier: 'Множитель веса',
preferredHoursHint:
'В выбранные часы вес шоу умножается — оно чаще попадает в эфир. Время в UTC.',
preferredNone: 'Окна не заданы — предпочтений по времени нет.',
preferredAddWindow: 'Добавить окно',
preferredBadRange: 'начало ≥ конца',
ads: 'Реклама',
pickAd: 'Выберите ролик',
noAds: 'Пул рекламы пуст',
@@ -523,8 +537,13 @@ const resources = {
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',
@@ -554,6 +573,8 @@ const resources = {
bumperTriggerOnShowChange: 'Show change',
bumperTriggerBetweenEpisodes: 'Between episodes',
bumperTriggerBoth: 'Both',
bumperVariantWeight: 'Weight',
bumperVariantWeightHint: 'For the “weighted random” strategy: higher = more often (0 — never picked)',
bumperDefault: 'default',
bumperSeconds: 's',
bumperDefaultDuration: '≈8 s (jingle)',
@@ -582,6 +603,13 @@ const resources = {
blockDuration: 'By time',
episodes: 'episodes',
minutes: 'minutes',
preferredHours: 'Hours',
preferredMultiplier: 'Weight multiplier',
preferredHoursHint:
'During the selected hours the shows weight is multiplied, so it airs more often. Times are UTC.',
preferredNone: 'No windows set — no time preference.',
preferredAddWindow: 'Add window',
preferredBadRange: 'start ≥ end',
ads: 'Ads',
pickAd: 'Pick an ad',
noAds: 'Ad pool is empty',