Add media and channel management routes: introduce new routes for admin channels, media, and shows in the routing structure. Update navigation in the admin layout to include links for these new sections. Enhance type definitions for media assets and shows in the API types. Integrate HLS.js for improved streaming support.
This commit is contained in:
@@ -36,3 +36,120 @@ export type PagedList<T> = {
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export type CreatedIdResponse = { id: string }
|
||||
|
||||
// ── Медиа ────────────────────────────────────────────────────────────────
|
||||
export type MediaAssetStatus = 'Pending' | 'Processing' | 'Ready' | 'Failed'
|
||||
export type MediaSource = 'Upload' | 'Inbox'
|
||||
|
||||
export type MediaAssetDto = {
|
||||
id: string
|
||||
originalFileName: string
|
||||
source: MediaSource
|
||||
status: MediaAssetStatus
|
||||
durationSeconds: number | null
|
||||
segmentCount: number | null
|
||||
width: number | null
|
||||
height: number | null
|
||||
videoCodec: string | null
|
||||
audioCodec: string | null
|
||||
errorMessage: string | null
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
// ── Библиотека (шоу) ───────────────────────────────────────────────────────
|
||||
export type ShowKind = 'Series' | 'Single'
|
||||
|
||||
export type ShowSummaryDto = {
|
||||
id: string
|
||||
name: string
|
||||
kind: ShowKind
|
||||
episodeCount: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export type EpisodeDto = {
|
||||
id: string
|
||||
mediaAssetId: string
|
||||
position: number
|
||||
assetName: string | null
|
||||
assetStatus: MediaAssetStatus | null
|
||||
durationSeconds: number | null
|
||||
}
|
||||
|
||||
export type ShowDto = {
|
||||
id: string
|
||||
name: string
|
||||
kind: ShowKind
|
||||
description: string | null
|
||||
episodes: EpisodeDto[]
|
||||
}
|
||||
|
||||
// ── Каналы ────────────────────────────────────────────────────────────────
|
||||
export type BlockMode = 'Count' | 'Duration'
|
||||
export type AdInsertion = 'BetweenBlocks' | 'BetweenEpisodes'
|
||||
export type OverrideMode = 'Exclusive' | 'Boost'
|
||||
export type ScheduleEntryKind = 'Program' | 'Ad'
|
||||
|
||||
export type ChannelSummaryDto = {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
isEnabled: boolean
|
||||
}
|
||||
|
||||
export type ChannelShowDto = {
|
||||
id: string
|
||||
showId: string
|
||||
showName: string
|
||||
weight: number
|
||||
blockMode: BlockMode
|
||||
blockValue: number
|
||||
isEnabled: boolean
|
||||
nextEpisodeIndex: number
|
||||
}
|
||||
|
||||
export type ChannelAdDto = {
|
||||
id: string
|
||||
mediaAssetId: string
|
||||
assetName: string | null
|
||||
position: number
|
||||
}
|
||||
|
||||
export type OverrideShowDto = { showId: string; showName: string; weight: number }
|
||||
|
||||
export type ProgrammingOverrideDto = {
|
||||
id: string
|
||||
mode: OverrideMode
|
||||
startsAtUtc: string
|
||||
endsAtUtc: string
|
||||
shows: OverrideShowDto[]
|
||||
}
|
||||
|
||||
export type ChannelDto = {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
isEnabled: boolean
|
||||
adInsertion: AdInsertion
|
||||
adsPerBreak: number
|
||||
fillerAssetId: string | null
|
||||
shows: ChannelShowDto[]
|
||||
ads: ChannelAdDto[]
|
||||
overrides: ProgrammingOverrideDto[]
|
||||
}
|
||||
|
||||
export type ScheduleEntryDto = {
|
||||
id: string
|
||||
kind: ScheduleEntryKind
|
||||
mediaAssetId: string
|
||||
startsAtUtc: string
|
||||
endsAtUtc: string
|
||||
showId: string | null
|
||||
showName: string | null
|
||||
episodeIndex: number | null
|
||||
}
|
||||
|
||||
// ── Публичный эфир ─────────────────────────────────────────────────────────
|
||||
export type PublicChannelDto = { id: string; slug: string; name: string }
|
||||
|
||||
@@ -57,6 +57,15 @@ const resources = {
|
||||
placeholder: 'Список каналов появится здесь позже — пока в эфире только тестовая заставка.',
|
||||
role: 'Роль',
|
||||
},
|
||||
air: {
|
||||
now: 'Сейчас',
|
||||
next: 'Далее',
|
||||
ad: 'Реклама',
|
||||
episode: 'Серия',
|
||||
noChannels: 'Пока нет доступных каналов. Загляните позже.',
|
||||
playbackError: 'Не удалось воспроизвести поток',
|
||||
unsupported: 'Браузер не поддерживает воспроизведение HLS',
|
||||
},
|
||||
settings: {
|
||||
title: 'Настройки аккаунта',
|
||||
changeUserName: 'Смена имени пользователя',
|
||||
@@ -91,6 +100,72 @@ const resources = {
|
||||
unblock: 'Разблокировать',
|
||||
filterAll: 'Все роли',
|
||||
},
|
||||
media: {
|
||||
title: 'Медиа',
|
||||
upload: 'Загрузить',
|
||||
uploaded: 'Файл загружен, идёт обработка',
|
||||
name: 'Файл',
|
||||
status: 'Статус',
|
||||
duration: 'Длительность',
|
||||
resolution: 'Разрешение',
|
||||
empty: 'Пока нет загруженных файлов',
|
||||
statuses: {
|
||||
Pending: 'В очереди',
|
||||
Processing: 'Обработка',
|
||||
Ready: 'Готов',
|
||||
Failed: 'Ошибка',
|
||||
},
|
||||
},
|
||||
shows: {
|
||||
title: 'Шоу',
|
||||
name: 'Название',
|
||||
kind: 'Тип',
|
||||
kinds: { Series: 'Сериал', Single: 'Полнометражка' },
|
||||
episodes: 'Серии',
|
||||
episode: 'Серия',
|
||||
addEpisode: 'Добавить серию',
|
||||
pickAsset: 'Выберите файл',
|
||||
noEpisodes: 'Серий пока нет',
|
||||
},
|
||||
channels: {
|
||||
title: 'Каналы',
|
||||
name: 'Название',
|
||||
slug: 'Slug',
|
||||
state: 'Состояние',
|
||||
enabled: 'В эфире',
|
||||
disabled: 'Выключен',
|
||||
enabledLabel: 'Канал в эфире',
|
||||
regenerate: 'Пересобрать',
|
||||
regenerated: 'Расписание пересобрано',
|
||||
settings: 'Настройки',
|
||||
adPolicy: 'Реклама',
|
||||
betweenBlocks: 'Между блоками',
|
||||
betweenEpisodes: 'Между сериями',
|
||||
adsPerBreak: 'Роликов подряд',
|
||||
filler: 'Заглушка',
|
||||
noFiller: 'Без заглушки',
|
||||
shows: 'Шоу канала',
|
||||
show: 'Шоу',
|
||||
weight: 'Вес',
|
||||
block: 'Блок',
|
||||
on: 'Вкл',
|
||||
noShows: 'Шоу не добавлены',
|
||||
pickShow: 'Выберите шоу',
|
||||
blockCount: 'По сериям',
|
||||
blockDuration: 'По времени',
|
||||
episodes: 'серий',
|
||||
minutes: 'минут',
|
||||
ads: 'Реклама',
|
||||
pickAd: 'Выберите ролик',
|
||||
noAds: 'Пул рекламы пуст',
|
||||
overrides: 'Марафоны / override',
|
||||
modes: { Exclusive: 'Эксклюзив', Boost: 'Буст' },
|
||||
from: 'С',
|
||||
to: 'По',
|
||||
noOverrides: 'Override не заданы',
|
||||
schedule: 'Расписание (12 ч)',
|
||||
noSchedule: 'Расписание ещё не построено — нажмите «Пересобрать»',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -149,6 +224,15 @@ const resources = {
|
||||
placeholder: 'The channel list will show up here later — for now, enjoy the test card.',
|
||||
role: 'Role',
|
||||
},
|
||||
air: {
|
||||
now: 'Now',
|
||||
next: 'Up next',
|
||||
ad: 'Ad',
|
||||
episode: 'Episode',
|
||||
noChannels: 'No channels available yet. Check back later.',
|
||||
playbackError: 'Failed to play the stream',
|
||||
unsupported: 'This browser cannot play HLS',
|
||||
},
|
||||
settings: {
|
||||
title: 'Account settings',
|
||||
changeUserName: 'Change username',
|
||||
@@ -183,6 +267,72 @@ const resources = {
|
||||
unblock: 'Unblock',
|
||||
filterAll: 'All roles',
|
||||
},
|
||||
media: {
|
||||
title: 'Media',
|
||||
upload: 'Upload',
|
||||
uploaded: 'File uploaded, processing started',
|
||||
name: 'File',
|
||||
status: 'Status',
|
||||
duration: 'Duration',
|
||||
resolution: 'Resolution',
|
||||
empty: 'No uploaded files yet',
|
||||
statuses: {
|
||||
Pending: 'Queued',
|
||||
Processing: 'Processing',
|
||||
Ready: 'Ready',
|
||||
Failed: 'Failed',
|
||||
},
|
||||
},
|
||||
shows: {
|
||||
title: 'Shows',
|
||||
name: 'Name',
|
||||
kind: 'Kind',
|
||||
kinds: { Series: 'Series', Single: 'Movie' },
|
||||
episodes: 'Episodes',
|
||||
episode: 'Episode',
|
||||
addEpisode: 'Add episode',
|
||||
pickAsset: 'Pick a file',
|
||||
noEpisodes: 'No episodes yet',
|
||||
},
|
||||
channels: {
|
||||
title: 'Channels',
|
||||
name: 'Name',
|
||||
slug: 'Slug',
|
||||
state: 'State',
|
||||
enabled: 'On air',
|
||||
disabled: 'Off',
|
||||
enabledLabel: 'Channel on air',
|
||||
regenerate: 'Rebuild',
|
||||
regenerated: 'Schedule rebuilt',
|
||||
settings: 'Settings',
|
||||
adPolicy: 'Ads',
|
||||
betweenBlocks: 'Between blocks',
|
||||
betweenEpisodes: 'Between episodes',
|
||||
adsPerBreak: 'Ads per break',
|
||||
filler: 'Filler',
|
||||
noFiller: 'No filler',
|
||||
shows: 'Channel shows',
|
||||
show: 'Show',
|
||||
weight: 'Weight',
|
||||
block: 'Block',
|
||||
on: 'On',
|
||||
noShows: 'No shows added',
|
||||
pickShow: 'Pick a show',
|
||||
blockCount: 'By episodes',
|
||||
blockDuration: 'By time',
|
||||
episodes: 'episodes',
|
||||
minutes: 'minutes',
|
||||
ads: 'Ads',
|
||||
pickAd: 'Pick an ad',
|
||||
noAds: 'Ad pool is empty',
|
||||
overrides: 'Marathons / overrides',
|
||||
modes: { Exclusive: 'Exclusive', Boost: 'Boost' },
|
||||
from: 'From',
|
||||
to: 'To',
|
||||
noOverrides: 'No overrides set',
|
||||
schedule: 'Schedule (12h)',
|
||||
noSchedule: 'Schedule not built yet — click “Rebuild”',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user