Add TV bumpers functionality: introduce configuration options for bumpers in .env.example and appsettings.json, enhance ChannelEndpoints to manage jingles and bumper assets, and update Channel and ScheduleEntry models to support bumper logic. Implement validation for bumper settings and integrate bumper handling in scheduling logic.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { apiRequest } from '@/shared/api/client'
|
||||
import { apiRequest, getAccessToken, HttpError } from '@/shared/api/client'
|
||||
import type {
|
||||
AdInsertion,
|
||||
BlockMode,
|
||||
BumperSettings,
|
||||
ChannelDto,
|
||||
ChannelSummaryDto,
|
||||
CreatedIdResponse,
|
||||
@@ -26,6 +27,8 @@ export type ChannelSettingsBody = {
|
||||
isEnabled: boolean
|
||||
adInsertion: AdInsertion
|
||||
adsPerBreak: number
|
||||
bumpersEnabled: boolean
|
||||
bumper: BumperSettings
|
||||
fillerAssetId: string | null
|
||||
}
|
||||
|
||||
@@ -67,6 +70,60 @@ export function removeChannelAd(id: string, channelAdId: string) {
|
||||
return apiRequest<void>(`/admin/channels/${id}/ads/${channelAdId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export function addChannelJingle(id: string, mediaAssetId: string) {
|
||||
return apiRequest<CreatedIdResponse>(`/admin/channels/${id}/jingles`, {
|
||||
method: 'POST',
|
||||
body: { mediaAssetId },
|
||||
})
|
||||
}
|
||||
|
||||
export function removeChannelJingle(id: string, channelJingleId: string) {
|
||||
return apiRequest<void>(`/admin/channels/${id}/jingles/${channelJingleId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
/** Загрузка сырого файла заставки (фон/музыка): тело — файл, имя — в query (как в uploadMedia). */
|
||||
function uploadBumperFile(id: string, kind: 'background' | 'music', file: File): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest()
|
||||
const query = new URLSearchParams({ fileName: file.name })
|
||||
xhr.open('PUT', `/api/admin/channels/${id}/bumper/${kind}?${query.toString()}`)
|
||||
const token = getAccessToken()
|
||||
if (token) xhr.setRequestHeader('Authorization', `Bearer ${token}`)
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve()
|
||||
} else {
|
||||
let detail = `HTTP ${xhr.status}`
|
||||
try {
|
||||
const problem = JSON.parse(xhr.responseText) as { detail?: string; title?: string }
|
||||
detail = problem.detail ?? problem.title ?? detail
|
||||
} catch {
|
||||
/* пусто */
|
||||
}
|
||||
reject(new HttpError({ detail }, xhr.status))
|
||||
}
|
||||
}
|
||||
xhr.onerror = () => reject(new HttpError({ title: 'Network error' }, 0))
|
||||
xhr.send(file)
|
||||
})
|
||||
}
|
||||
|
||||
export function uploadBumperBackground(id: string, file: File) {
|
||||
return uploadBumperFile(id, 'background', file)
|
||||
}
|
||||
|
||||
export function uploadBumperMusic(id: string, file: File) {
|
||||
return uploadBumperFile(id, 'music', file)
|
||||
}
|
||||
|
||||
export function clearBumperBackground(id: string) {
|
||||
return apiRequest<void>(`/admin/channels/${id}/bumper/background`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export function clearBumperMusic(id: string) {
|
||||
return apiRequest<void>(`/admin/channels/${id}/bumper/music`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
export type OverrideBody = {
|
||||
mode: OverrideMode
|
||||
startsAtUtc: string
|
||||
|
||||
Reference in New Issue
Block a user