Implement background clip functionality for bumpers
ci / build-backend (push) Successful in 2m14s
ci / build-frontend (push) Successful in 49s
ci / tests (push) Successful in 2m7s
ci / sonar (push) Successful in 7m39s

Enhanced the bumper system to support a new background type, 'Clip', allowing text to be overlaid on existing video clips. This update includes the addition of a BackgroundClipShowId property in various records and classes, ensuring that the clip's duration dictates the bumper length. Updated validation rules, mapping, and rendering logic to accommodate this new feature. Localization strings were also updated to reflect the new background option and its implications for audio handling.
This commit is contained in:
Leonid Pershin
2026-07-29 23:54:11 +03:00
parent 35cb4b09dc
commit 6be373476c
26 changed files with 1798 additions and 24 deletions
@@ -1,4 +1,4 @@
import { useMutation } from '@tanstack/react-query'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import type {
@@ -13,13 +13,15 @@ import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/shared/ui/select'
import { toast } from '@/shared/ui/toast-store'
import { listInterstitials } from '@/features/admin/interstitials/api'
import { qk } from '@/shared/api/query-keys'
import { removeBumperVariant, updateBumperVariant } from '../api'
import { unknownTokens } from '../placeholders'
import { BumperFramePreview } from './BumperFramePreview'
import { BumperLinesEditor } from './BumperLinesEditor'
const TRIGGERS: BumperTrigger[] = ['OnShowChange', 'BetweenEpisodes', 'Both']
const BACKGROUNDS: BumperBackground[] = ['Template', 'NextPoster', 'NowPoster']
const BACKGROUNDS: BumperBackground[] = ['Template', 'NextPoster', 'NowPoster', 'Clip']
/**
* Подблок: слева строки, справа постоянный предпросмотр кадра. Предпросмотр не опциональный —
@@ -39,10 +41,16 @@ export function BumperVariantEditor({
onError: (e: unknown) => void
}>) {
const { t } = useTranslation()
// Ролики грузим списком: их немного, а выбирать подложку удобнее из готового перечня.
const { data: clips } = useQuery({
queryKey: qk.interstitials.all,
queryFn: listInterstitials,
})
const [form, setForm] = useState({
name: variant.name,
trigger: variant.trigger,
background: variant.background,
backgroundClipShowId: variant.backgroundClipShowId,
weight: variant.weight,
lines: variant.lines,
})
@@ -52,6 +60,7 @@ export function BumperVariantEditor({
name: variant.name,
trigger: variant.trigger,
background: variant.background,
backgroundClipShowId: variant.backgroundClipShowId,
weight: variant.weight,
lines: variant.lines,
})
@@ -128,6 +137,31 @@ export function BumperVariantEditor({
</SelectContent>
</Select>
</div>
{/* Ролик-подложка — только при своём источнике фона: в остальных случаях поле было бы
настройкой, которая ни на что не влияет. */}
{form.background === 'Clip' && (
<div className="flex flex-col gap-1.5">
<Label>{t('admin.bumpers.backgroundClip')}</Label>
<Select
value={form.backgroundClipShowId ?? ''}
onValueChange={(v) => set('backgroundClipShowId', v)}
>
<SelectTrigger>
<SelectValue placeholder={t('admin.bumpers.pickClip')} />
</SelectTrigger>
<SelectContent>
{(clips ?? []).map((clip) => (
<SelectItem key={clip.id} value={clip.id}>
{clip.name}
</SelectItem>
))}
</SelectContent>
</Select>
<span className="text-xs text-muted-foreground">
{t('admin.bumpers.backgroundClipHint')}
</span>
</div>
)}
<div className="flex flex-col gap-1.5">
<Label>{t('admin.bumpers.weight')}</Label>
<Input