Refactor media import handlers to utilize ManualInboxTaker for improved functionality
ci / build-backend (push) Successful in 1m36s
ci / build-frontend (push) Successful in 57s
ci / tests (push) Successful in 1m36s
ci / sonar (push) Successful in 4m40s

Updated the ImportManualInboxCommandHandler and ImportMoviesCommandHandler to replace direct storage interactions with the ManualInboxTaker, streamlining the import process. This change enhances code clarity and maintains consistency across media import functionalities. Additionally, refactored related tests to accommodate the new handler structure, ensuring robust testing of the import logic.
This commit is contained in:
Leonid Pershin
2026-07-28 10:46:21 +03:00
parent 287ed5aa12
commit 6ca629c13a
10 changed files with 204 additions and 219 deletions
@@ -0,0 +1,50 @@
import { useTranslation } from 'react-i18next'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
/**
* Поля разбора серий: ручной сезон и regex номера. Один компонент на оба разбора — из `manual/`
* и при загрузке файлов, — потому что правила у них общие: что показано в предпросмотре, то
* и уедет на сервер, и расходиться этим двум местам нельзя.
*/
export function EpisodeParseFields({
season,
onSeason,
regex,
onRegex,
regexOk,
}: Readonly<{
season: string
onSeason: (value: string) => void
regex: string
onRegex: (value: string) => void
/** Регулярка разбирается — иначе поле подсвечивается и снизу появляется объяснение. */
regexOk: boolean
}>) {
const { t } = useTranslation()
return (
<>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowSeason')}</Label>
{/* Ноль — законный сезон: в нём живут спецвыпуски и пилоты. */}
<Input
type="number"
min={0}
placeholder={t('admin.media.toShowAuto')}
value={season}
onChange={(e) => onSeason(e.target.value)}
/>
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowRegex')}</Label>
<Input
placeholder="^(\d+)"
value={regex}
onChange={(e) => onRegex(e.target.value)}
className={!regexOk ? 'border-red-500' : undefined}
/>
</div>
</>
)
}
@@ -22,6 +22,7 @@ import {
parseSeasonFromFolder,
} from './episode-parse'
import { buildEpisodeRegex, findNumbers, REGEX_PRESETS } from './episode-regex'
import { EpisodeParseFields } from './EpisodeParseFields'
import { matchShowByName } from './match-show'
/** Байты → «1,4 ГБ»: в ручном разборе размер — главный ориентир, что это за файл. */
@@ -250,26 +251,13 @@ export function ManualInboxPanel({ onClose }: Readonly<{ onClose: () => void }>)
<Label>{t('common.search')}</Label>
<Input value={query} onChange={(e) => setQuery(e.target.value)} />
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowSeason')}</Label>
{/* Ноль — законный сезон: в нём живут спецвыпуски и пилоты. */}
<Input
type="number"
min={0}
placeholder={t('admin.media.toShowAuto')}
value={seasonStr}
onChange={(e) => setSeasonStr(e.target.value)}
/>
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowRegex')}</Label>
<Input
placeholder="^(\d+)"
value={regexStr}
onChange={(e) => setRegexStr(e.target.value)}
className={!regexOk ? 'border-red-500' : undefined}
/>
</div>
<EpisodeParseFields
season={seasonStr}
onSeason={setSeasonStr}
regex={regexStr}
onRegex={setRegexStr}
regexOk={regexOk}
/>
</div>
{!regexOk && <p className="text-xs text-red-500">{t('admin.media.toShowRegexInvalid')}</p>}
@@ -6,11 +6,10 @@ import { qk } from '@/shared/api/query-keys'
import { Badge } from '@/shared/ui/badge'
import { Button } from '@/shared/ui/button'
import { DialogFooter } from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { listShows } from '@/features/admin/shows/api'
import { ShowPicker } from '@/features/admin/shows/ShowPicker'
import { compareParsed, formatSeasonEpisode, isValidRegex, parseEpisodeName } from './episode-parse'
import { EpisodeParseFields } from './EpisodeParseFields'
import { matchShowByName } from './match-show'
import { useUploadStore } from './upload-store'
@@ -101,25 +100,13 @@ export function UploadToShowPanel({ onClose }: Readonly<{ onClose: () => void }>
<div className="flex min-h-0 flex-1 flex-col gap-4">
<div className="grid gap-3 sm:grid-cols-2">
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowSeason')}</Label>
<Input
type="number"
min={1}
placeholder={t('admin.media.toShowAuto')}
value={seasonStr}
onChange={(e) => setSeasonStr(e.target.value)}
/>
</div>
<div className="flex flex-col gap-1.5">
<Label>{t('admin.media.toShowRegex')}</Label>
<Input
placeholder="^(\d+)"
value={regexStr}
onChange={(e) => setRegexStr(e.target.value)}
className={!regexOk ? 'border-red-500' : undefined}
/>
</div>
<EpisodeParseFields
season={seasonStr}
onSeason={setSeasonStr}
regex={regexStr}
onRegex={setRegexStr}
regexOk={regexOk}
/>
</div>
<p className="text-xs text-muted-foreground">
{t('admin.media.toShowHint')}