Enhance ScheduleEntryDto and related components to support franchise collection details
Updated ScheduleEntryDto to include properties for collection part, total parts, and collection name, allowing for better representation of franchise information. Modified GetChannelScheduleQueryHandler to populate these new fields based on collection data. Adjusted SchedulePreview component to prioritize collection part display over episode information. Enhanced localization strings to support new collection-related terms in both English and Russian.
This commit is contained in:
@@ -34,13 +34,27 @@ function EntryLabel({ entry }: Readonly<{ entry: ScheduleEntryDto }>) {
|
||||
)
|
||||
}
|
||||
|
||||
/** «· S02E05» либо «· серия N» — что удалось распознать; ничего, если ни того ни другого нет. */
|
||||
/** «· S02E05», «· часть 3 из 3» либо «· серия N» — что удалось распознать; иначе ничего. */
|
||||
function EpisodeSuffix({ entry }: Readonly<{ entry: ScheduleEntryDto }>) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (entry.seasonEpisode)
|
||||
return <span className="text-muted-foreground"> · {entry.seasonEpisode}</span>
|
||||
|
||||
// Часть франшизы важнее номера серии и идёт раньше него: у фильма серия всегда одна, и «Серия 1»
|
||||
// не говорит ничего, а «часть 3 из 3» сразу объясняет, где мы в трилогии.
|
||||
if (entry.collectionPart != null && entry.collectionParts != null)
|
||||
return (
|
||||
<span className="text-muted-foreground" title={entry.collectionName ?? undefined}>
|
||||
{' '}
|
||||
·{' '}
|
||||
{t('air.collectionPart', {
|
||||
part: entry.collectionPart,
|
||||
total: entry.collectionParts,
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
|
||||
if (entry.episodeIndex == null) return null
|
||||
|
||||
return (
|
||||
|
||||
@@ -20,6 +20,14 @@ function parseBuiltin(name: string): ParsedEpisode {
|
||||
const nx = /(?:^|[^\d])(\d{1,2})x(\d{1,3})(?:[^\d]|$)/i.exec(name)
|
||||
if (nx) return { season: Number(nx[1]), episode: Number(nx[2]) }
|
||||
|
||||
// Производственный код: «2ACV01» (Футурама), «1ACX05» (Гриффины), «1ACG02» (Разочарование).
|
||||
// Сезон слева, номер серии справа, между ними буквенный код студии.
|
||||
//
|
||||
// Регистр здесь принципиален и флага `i` нет: код всегда прописной, а со строчными буквами
|
||||
// под шаблон попало бы «Part1of10» — «1» сезоном, «10» серией.
|
||||
const production = /(?:^|[^\dA-Za-z])(\d{1,2})[A-Z]{2,4}(\d{2,3})(?:[^\d]|$)/.exec(name)
|
||||
if (production) return { season: Number(production[1]), episode: Number(production[2]) }
|
||||
|
||||
// Ведущий номер серии: «01. Название», «02 - Название», «03_Название», «4) Название».
|
||||
const lead = /^\s*(\d{1,3})[\s._)\]-]/.exec(name)
|
||||
return { season: null, episode: lead ? Number(lead[1]) : null }
|
||||
|
||||
@@ -3,6 +3,8 @@ export const REGEX_PRESETS: { key: string; pattern: string }[] = [
|
||||
{ key: 'seriesWord', pattern: String.raw`[Сс]ерия\s*(\d{1,3})` },
|
||||
{ key: 'episodeWord', pattern: String.raw`[Ээ]пизод\s*(\d{1,3})` },
|
||||
{ key: 'seasonEpisode', pattern: String.raw`[Ss](\d{1,2})[Ee](\d{1,3})` },
|
||||
// Производственный код: «2ACV01». Две группы — из него читаются и сезон, и серия.
|
||||
{ key: 'productionCode', pattern: String.raw`(\d{1,2})[A-Z]{1,4}(\d{2,3})` },
|
||||
{ key: 'afterDash', pattern: String.raw`[-–—]\s*(\d{1,3})` },
|
||||
{ key: 'firstNumber', pattern: String.raw`(?:^|\D)(\d{1,3})(?:\D|$)` },
|
||||
]
|
||||
|
||||
@@ -975,6 +975,10 @@ export type ScheduleEntryDto = {
|
||||
/** Для заставок: имя подблока и его текст — для метки в расписании. */
|
||||
bumperName: string | null
|
||||
bumperText: string | null
|
||||
/** Какой частью франшизы шла запись и сколько их всего; null — шло само по себе. */
|
||||
collectionPart: number | null
|
||||
collectionParts: number | null
|
||||
collectionName: string | null
|
||||
}
|
||||
|
||||
// ── Публичный эфир ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -59,6 +59,7 @@ export const en = {
|
||||
ad: 'Ad',
|
||||
bumper: 'Bumper',
|
||||
episode: 'Episode',
|
||||
collectionPart: 'part {{part}} of {{total}}',
|
||||
volume: 'Volume',
|
||||
live: 'Live',
|
||||
noChannels: 'No channels available yet. Check back later.',
|
||||
@@ -318,6 +319,7 @@ export const en = {
|
||||
seriesWord: 'Серия N',
|
||||
episodeWord: 'Эпизод N',
|
||||
seasonEpisode: 'SxxEyy',
|
||||
productionCode: '2ACV01',
|
||||
afterDash: 'after a dash',
|
||||
firstNumber: 'first number',
|
||||
},
|
||||
|
||||
@@ -59,6 +59,7 @@ export const ru = {
|
||||
ad: 'Реклама',
|
||||
bumper: 'Заставка',
|
||||
episode: 'Серия',
|
||||
collectionPart: 'часть {{part}} из {{total}}',
|
||||
live: 'В эфире',
|
||||
volume: 'Громкость',
|
||||
noChannels: 'Пока нет доступных каналов. Загляните позже.',
|
||||
@@ -315,6 +316,7 @@ export const ru = {
|
||||
seriesWord: 'Серия N',
|
||||
episodeWord: 'Эпизод N',
|
||||
seasonEpisode: 'SxxEyy',
|
||||
productionCode: '2ACV01',
|
||||
afterDash: 'после тире',
|
||||
firstNumber: 'первое число',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user