0.16.1-7 Release-Bugfixes
This commit is contained in:
@@ -154,6 +154,59 @@ function deriveAudioTracks(plan) {
|
||||
return [];
|
||||
}
|
||||
|
||||
function normalizePositiveInt(value) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return null;
|
||||
}
|
||||
return Math.trunc(parsed);
|
||||
}
|
||||
|
||||
function buildVideoMetadataPayload(sourceMetadata, fallbackTitle = '') {
|
||||
const source = sourceMetadata && typeof sourceMetadata === 'object' ? sourceMetadata : {};
|
||||
const normalized = {
|
||||
title: String(source?.title || fallbackTitle || '').trim() || null,
|
||||
year: Number.isFinite(Number(source?.year)) ? Math.trunc(Number(source.year)) : null,
|
||||
imdbId: String(source?.imdbId || '').trim() || null,
|
||||
poster: String(source?.poster || '').trim() || null
|
||||
};
|
||||
|
||||
const metadataProvider = String(source?.metadataProvider || '').trim() || null;
|
||||
const workflowKind = String(source?.workflowKind || '').trim() || null;
|
||||
const providerId = String(source?.providerId || '').trim() || null;
|
||||
const metadataKind = String(source?.metadataKind || '').trim() || null;
|
||||
const seasonName = String(source?.seasonName || '').trim() || null;
|
||||
const imdbRating = String(source?.imdbRating || '').trim() || null;
|
||||
const tmdbId = normalizePositiveInt(source?.tmdbId);
|
||||
const seasonNumber = normalizePositiveInt(source?.seasonNumber);
|
||||
const discNumber = normalizePositiveInt(source?.discNumber);
|
||||
const episodeCount = Number(source?.episodeCount || 0);
|
||||
const voteAverageRaw = Number(source?.voteAverage || 0);
|
||||
const voteAverage = Number.isFinite(voteAverageRaw) && voteAverageRaw > 0
|
||||
? Number(voteAverageRaw.toFixed(1))
|
||||
: null;
|
||||
const episodes = Array.isArray(source?.episodes) ? source.episodes : [];
|
||||
const tmdbDetails = source?.tmdbDetails && typeof source.tmdbDetails === 'object' && !Array.isArray(source.tmdbDetails)
|
||||
? source.tmdbDetails
|
||||
: null;
|
||||
|
||||
if (metadataProvider) normalized.metadataProvider = metadataProvider;
|
||||
if (workflowKind) normalized.workflowKind = workflowKind;
|
||||
if (providerId) normalized.providerId = providerId;
|
||||
if (metadataKind) normalized.metadataKind = metadataKind;
|
||||
if (tmdbId) normalized.tmdbId = tmdbId;
|
||||
if (seasonNumber) normalized.seasonNumber = seasonNumber;
|
||||
if (seasonName) normalized.seasonName = seasonName;
|
||||
if (discNumber) normalized.discNumber = discNumber;
|
||||
if (Number.isFinite(episodeCount) && episodeCount > 0) normalized.episodeCount = Math.trunc(episodeCount);
|
||||
if (episodes.length > 0) normalized.episodes = episodes;
|
||||
if (voteAverage !== null) normalized.voteAverage = voteAverage;
|
||||
if (imdbRating) normalized.imdbRating = imdbRating;
|
||||
if (tmdbDetails) normalized.tmdbDetails = tmdbDetails;
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
// ── Status helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
function mediaTypeBadge(type) {
|
||||
@@ -260,10 +313,10 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
|
||||
// ── Video metadata ──────────────────────────────────────────────────────────
|
||||
const [videoMetadata, setVideoMetadata] = useState(() => ({
|
||||
title: String(planMetadata?.title || job.title || job.detected_title || '').trim(),
|
||||
year: Number.isFinite(Number(planMetadata?.year)) ? Math.trunc(Number(planMetadata.year)) : null,
|
||||
imdbId: String(planMetadata?.imdbId || '').trim() || null,
|
||||
poster: String(planMetadata?.poster || '').trim() || null
|
||||
...buildVideoMetadataPayload(
|
||||
planMetadata,
|
||||
String(job.title || job.detected_title || '').trim()
|
||||
)
|
||||
}));
|
||||
|
||||
// ── Script / Chain catalog ──────────────────────────────────────────────────
|
||||
@@ -378,10 +431,10 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
coverUrl: coverUrl || null
|
||||
}
|
||||
: {
|
||||
title: String(videoMetadata?.title || job.title || job.detected_title || '').trim() || null,
|
||||
year: Number.isFinite(Number(videoMetadata?.year)) ? Math.trunc(Number(videoMetadata.year)) : null,
|
||||
imdbId: String(videoMetadata?.imdbId || '').trim() || null,
|
||||
poster: String(videoMetadata?.poster || '').trim() || null
|
||||
...buildVideoMetadataPayload(
|
||||
videoMetadata,
|
||||
String(job.title || job.detected_title || '').trim()
|
||||
)
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -436,12 +489,13 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
jobId: job.id,
|
||||
detectedTitle: videoMetadata?.title || job.title || job.detected_title || '',
|
||||
selectedMetadata: {
|
||||
...videoMetadata,
|
||||
title: videoMetadata?.title || job.title || job.detected_title || '',
|
||||
year: videoMetadata?.year || null,
|
||||
imdbId: videoMetadata?.imdbId || null,
|
||||
poster: videoMetadata?.poster || null
|
||||
},
|
||||
omdbCandidates: []
|
||||
metadataCandidates: []
|
||||
}), [job.id, job.title, job.detected_title, videoMetadata]);
|
||||
|
||||
const cdMetadataDialogContext = useMemo(() => ({
|
||||
@@ -454,31 +508,28 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
}), [job.id, audioTracks, trackFields]);
|
||||
|
||||
// ── Dialog handlers ─────────────────────────────────────────────────────────
|
||||
const handleOmdbSearch = async (query) => {
|
||||
const handleMetadataSearch = async (query) => {
|
||||
try {
|
||||
const response = await api.searchOmdb(query);
|
||||
const response = await api.searchTmdbMovie(query);
|
||||
return Array.isArray(response?.results) ? response.results : [];
|
||||
} catch { return []; }
|
||||
};
|
||||
|
||||
const handleOmdbSubmit = async (payload) => {
|
||||
const handleMetadataSubmit = async (payload) => {
|
||||
setSearchDialogBusy(true);
|
||||
try {
|
||||
setVideoMetadata({
|
||||
title: String(payload?.title || job.title || job.detected_title || '').trim(),
|
||||
year: Number.isFinite(Number(payload?.year)) ? Math.trunc(Number(payload.year)) : null,
|
||||
imdbId: String(payload?.imdbId || '').trim() || null,
|
||||
poster: String(payload?.poster || '').trim() || null
|
||||
});
|
||||
setVideoMetadata(buildVideoMetadataPayload(payload, String(job.title || job.detected_title || '').trim()));
|
||||
setMetadataDialogVisible(false);
|
||||
} finally {
|
||||
setSearchDialogBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMusicBrainzSearchDialog = async (query) => {
|
||||
const handleMusicBrainzSearchDialog = async (query, options = {}) => {
|
||||
try {
|
||||
const response = await api.searchMusicBrainz(query);
|
||||
const response = await api.searchMusicBrainz(query, {
|
||||
trackCount: Number(options?.trackCount || 0) > 0 ? Math.trunc(Number(options.trackCount)) : null
|
||||
});
|
||||
return Array.isArray(response?.results) ? response.results : [];
|
||||
} catch { return []; }
|
||||
};
|
||||
@@ -642,10 +693,10 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
coverUrl: coverUrl || null
|
||||
}
|
||||
: {
|
||||
title: String(videoMetadata?.title || job.title || job.detected_title || '').trim() || null,
|
||||
year: Number.isFinite(Number(videoMetadata?.year)) ? Math.trunc(Number(videoMetadata.year)) : null,
|
||||
imdbId: String(videoMetadata?.imdbId || '').trim() || null,
|
||||
poster: String(videoMetadata?.poster || '').trim() || null
|
||||
...buildVideoMetadataPayload(
|
||||
videoMetadata,
|
||||
String(job.title || job.detected_title || '').trim()
|
||||
)
|
||||
};
|
||||
|
||||
await api.startConverterJob(job.id, {
|
||||
@@ -880,8 +931,10 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
)}
|
||||
|
||||
{/* Pre / Post Ausführungen */}
|
||||
{renderEncodeSection('pre', preItems)}
|
||||
{renderEncodeSection('post', postItems)}
|
||||
<div className="encode-automation-grid">
|
||||
{renderEncodeSection('pre', preItems)}
|
||||
{renderEncodeSection('post', postItems)}
|
||||
</div>
|
||||
|
||||
{/* Aktionen */}
|
||||
<div className="actions-row" style={{ marginTop: '1rem' }}>
|
||||
@@ -1025,8 +1078,8 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
visible={metadataDialogVisible}
|
||||
context={metadataDialogContext}
|
||||
onHide={() => setMetadataDialogVisible(false)}
|
||||
onSubmit={handleOmdbSubmit}
|
||||
onSearch={handleOmdbSearch}
|
||||
onSubmit={handleMetadataSubmit}
|
||||
onSearch={handleMetadataSearch}
|
||||
busy={searchDialogBusy}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user