UI/Features

This commit is contained in:
2026-03-13 11:07:34 +00:00
parent 7948dd298c
commit 5b41f728c5
28 changed files with 5690 additions and 936 deletions

View File

@@ -243,8 +243,23 @@ function renderTemplate(template, values) {
});
}
function buildOutputPathPreview(settings, metadata, fallbackJobId = null) {
const movieDir = String(settings?.movie_dir || '').trim();
function resolveProfiledSetting(settings, key, mediaProfile) {
const profileKey = mediaProfile ? `${key}_${mediaProfile}` : null;
if (profileKey && settings?.[profileKey] != null && settings[profileKey] !== '') {
return settings[profileKey];
}
const fallbackProfiles = mediaProfile === 'bluray' ? ['dvd'] : ['bluray'];
for (const fb of fallbackProfiles) {
const fbKey = `${key}_${fb}`;
if (settings?.[fbKey] != null && settings[fbKey] !== '') {
return settings[fbKey];
}
}
return settings?.[key] ?? null;
}
function buildOutputPathPreview(settings, mediaProfile, metadata, fallbackJobId = null) {
const movieDir = String(resolveProfiledSetting(settings, 'movie_dir', mediaProfile) || '').trim();
if (!movieDir) {
return null;
}
@@ -252,13 +267,26 @@ function buildOutputPathPreview(settings, metadata, fallbackJobId = null) {
const title = metadata?.title || (fallbackJobId ? `job-${fallbackJobId}` : 'job');
const year = metadata?.year || new Date().getFullYear();
const imdbId = metadata?.imdbId || (fallbackJobId ? `job-${fallbackJobId}` : 'noimdb');
const fileTemplate = settings?.filename_template || '${title} (${year})';
const folderTemplate = String(settings?.output_folder_template || '').trim() || fileTemplate;
const folderName = sanitizeFileName(renderTemplate(folderTemplate, { title, year, imdbId }));
const baseName = sanitizeFileName(renderTemplate(fileTemplate, { title, year, imdbId }));
const ext = String(settings?.output_extension || 'mkv').trim() || 'mkv';
const DEFAULT_TEMPLATE = '${title} (${year})/${title} (${year})';
const rawTemplate = resolveProfiledSetting(settings, 'output_template', mediaProfile);
const template = String(rawTemplate || DEFAULT_TEMPLATE).trim() || DEFAULT_TEMPLATE;
const rendered = renderTemplate(template, { title, year, imdbId });
const segments = rendered
.replace(/\\/g, '/')
.replace(/\/+/g, '/')
.replace(/^\/+|\/+$/g, '')
.split('/')
.map((seg) => sanitizeFileName(seg))
.filter(Boolean);
const baseName = segments.length > 0 ? segments[segments.length - 1] : 'untitled';
const folderParts = segments.slice(0, -1);
const rawExt = resolveProfiledSetting(settings, 'output_extension', mediaProfile);
const ext = String(rawExt || 'mkv').trim() || 'mkv';
const root = movieDir.replace(/\/+$/g, '');
return `${root}/${folderName}/${baseName}.${ext}`;
if (folderParts.length > 0) {
return `${root}/${folderParts.join('/')}/${baseName}.${ext}`;
}
return `${root}/${baseName}.${ext}`;
}
export default function PipelineStatusCard({
@@ -515,8 +543,8 @@ export default function PipelineStatusCard({
const playlistDecisionRequiredBeforeStart = state === 'WAITING_FOR_USER_DECISION';
const commandOutputPath = useMemo(
() => buildOutputPathPreview(settingsMap, selectedMetadata, retryJobId),
[settingsMap, selectedMetadata, retryJobId]
() => buildOutputPathPreview(settingsMap, jobMediaProfile, selectedMetadata, retryJobId),
[settingsMap, jobMediaProfile, selectedMetadata, retryJobId]
);
const presetDisplayValue = useMemo(() => {
const preset = String(mediaInfoReview?.selectors?.preset || '').trim();