Implement movie import functionality and enhance media processing
ci / build-backend (push) Successful in 1m34s
ci / build-frontend (push) Successful in 54s
ci / tests (push) Successful in 1m43s
ci / sonar (push) Successful in 6m45s

Added new endpoints for matching and importing movies, allowing for automated processing of film files from the inbox. Introduced logic to parse file names into titles and years, and integrated metadata matching to streamline the import process. Updated the MediaOptions to include a configuration for automatic movie creation from recognized files. Enhanced the InboxScanner to attempt movie attachment upon asset registration, improving user experience and efficiency. Updated documentation to reflect these new features and their usage.
This commit is contained in:
Leonid Pershin
2026-07-28 03:02:30 +03:00
parent e48838b69d
commit d859a9894c
25 changed files with 2144 additions and 4 deletions
+38
View File
@@ -81,6 +81,44 @@ export type ManualImportItem = {
episode: number | null
}
/** Кандидат из источника метаданных — то же, что показывает поиск на карточке шоу. */
export type MetadataCandidateDto = {
externalId: string
title: string
year: number | null
overview: string | null
posterUrl: string | null
}
export type MovieMatchStatus = 'Confident' | 'Uncertain' | 'NotFound' | 'AlreadyInLibrary'
/** Строка разбора фильмов: что распозналось в имени и что по нему нашлось. */
export type MovieMatchDto = {
name: string
parsedTitle: string
parsedYear: number | null
status: MovieMatchStatus
candidates: MetadataCandidateDto[]
pickedExternalId: string | null
existingShowId: string | null
existingShowName: string | null
}
/** Фильм к заведению: файл берётся из manual/ (relativePath) либо уже загружен (assetId). */
export type MovieImportItem = {
relativePath: string | null
assetId: string | null
title: string
year: number | null
externalId: string | null
}
export type ImportMoviesResultDto = {
created: number
enriched: number
failed: { title: string; reason: string }[]
}
export type ManualInboxListDto = {
files: ManualInboxFileDto[]
/** Выдача обрезана лимитом — в каталоге есть ещё. */
+29
View File
@@ -245,10 +245,39 @@ export const en = {
create: 'Create',
created: 'User created',
},
movies: {
title: 'Movie import',
hint: 'The file name is parsed into a title and a year, and the movie is looked up in the metadata source. Every file becomes its own show; fix the rows where the match is not exact.',
sources: { manual: 'From manual folder', disk: 'From my disk' },
pickFiles: 'Pick files',
scan: 'Parse ({{count}})',
scanning: 'Searching the source…',
nothingYet: 'Pick a source and press "Parse".',
noProvider: 'No metadata source configured — there is nothing to search with.',
file: 'File',
parsed: 'Title and year',
candidate: 'Candidate',
year: 'Year',
research: 'Search again',
empty: 'nothing found',
noCandidate: 'no metadata',
import: 'Create ({{count}})',
imported: 'Movies created: {{created}}, with metadata: {{enriched}}',
uploadStarted: 'Files are uploading — movies appear as they finish',
collectionsHint:
'Franchises turn into collections after the import — the suggestions wait on the Collections tab.',
statuses: {
Confident: 'exact match',
Uncertain: 'check this',
NotFound: 'not found — type the title',
AlreadyInLibrary: 'already in the library',
},
},
media: {
title: 'Media',
upload: 'Upload',
manualButton: 'From manual folder',
moviesButton: 'Movies',
manualTitle: 'Manual pick from the manual folder',
manualHint:
'Files in manual/ are not picked up by the scanner — select the ones you need and choose a show. Imported files leave the folder, just like from inbox.',
+29
View File
@@ -246,10 +246,39 @@ export const ru = {
create: 'Создать',
created: 'Пользователь создан',
},
movies: {
title: 'Разбор фильмов',
hint: 'Имя файла разбирается на название и год, по ним ищется фильм в источнике метаданных. Каждый файл становится отдельным шоу; строки, где совпадение неточное, поправьте руками.',
sources: { manual: 'Из папки manual', disk: 'С моего диска' },
pickFiles: 'Выбрать файлы',
scan: 'Разобрать ({{count}})',
scanning: 'Ищем в источнике…',
nothingYet: 'Выберите источник и нажмите «Разобрать».',
noProvider: 'Источник метаданных не настроен — без него искать нечем.',
file: 'Файл',
parsed: 'Название и год',
candidate: 'Кандидат',
year: 'Год',
research: 'Искать заново',
empty: 'ничего не нашлось',
noCandidate: 'без метаданных',
import: 'Завести ({{count}})',
imported: 'Заведено фильмов: {{created}}, с метаданными: {{enriched}}',
uploadStarted: 'Файлы пошли на загрузку — фильмы заведутся по мере готовности',
collectionsHint:
'Франшизы после импорта соберутся в коллекции — предложения ждут на вкладке «Коллекции».',
statuses: {
Confident: 'совпадение точное',
Uncertain: 'проверьте',
NotFound: 'не найдено — впишите название',
AlreadyInLibrary: 'уже в библиотеке',
},
},
media: {
title: 'Медиа',
upload: 'Загрузить',
manualButton: 'Из папки manual',
moviesButton: 'Фильмы',
manualTitle: 'Ручной разбор папки manual',
manualHint:
'Файлы из manual/ не разбираются сканером — выберите нужные и укажите шоу. Импортированные файлы уходят из папки, как и из inbox.',