0.13.1-7 Fix Series
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1-6",
|
||||
"version": "0.13.1-7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1-6",
|
||||
"version": "0.13.1-7",
|
||||
"dependencies": {
|
||||
"primeicons": "^7.0.0",
|
||||
"primereact": "^10.9.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripster-frontend",
|
||||
"version": "0.13.1-6",
|
||||
"version": "0.13.1-7",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -940,9 +940,9 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
|
||||
{isVideo && (
|
||||
<div className="converter-job-card-actions" style={{ marginTop: 0 }}>
|
||||
<Button
|
||||
label="OMDb neu zuordnen"
|
||||
label="Metadaten neu zuweisen"
|
||||
icon="pi pi-search"
|
||||
severity="secondary"
|
||||
severity="info"
|
||||
outlined
|
||||
size="small"
|
||||
onClick={() => setMetadataDialogVisible(true)}
|
||||
|
||||
@@ -1130,6 +1130,7 @@ export default function PipelineStatusCard({
|
||||
onRetry,
|
||||
onDeleteJob,
|
||||
onInputsChanged,
|
||||
jobRow,
|
||||
isQueued = false,
|
||||
busy
|
||||
}) {
|
||||
@@ -1141,6 +1142,21 @@ export default function PipelineStatusCard({
|
||||
const contextJobId = normalizeJobId(pipeline?.context?.jobId);
|
||||
const retryJobId = explicitJobId || contextJobId;
|
||||
const queueLocked = Boolean(isQueued && retryJobId);
|
||||
const orphanSource = String(
|
||||
jobRow?.makemkvInfo?.source
|
||||
|| pipeline?.context?.source
|
||||
|| pipeline?.context?.importSource
|
||||
|| ''
|
||||
).trim().toLowerCase();
|
||||
const isOrphanImport = orphanSource === 'orphan_raw_import';
|
||||
const isOrphanCancelled = isOrphanImport && (state === 'ERROR' || state === 'CANCELLED');
|
||||
const ripCompleted = Boolean(
|
||||
Number(jobRow?.rip_successful || jobRow?.ripSuccessful || pipeline?.context?.ripSuccessful || 0) === 1
|
||||
|| jobRow?.raw_path
|
||||
|| pipeline?.context?.rawPath
|
||||
|| jobRow?.makemkvInfo?.rawPath
|
||||
|| String(jobRow?.makemkvInfo?.status || '').trim().toUpperCase() === 'SUCCESS'
|
||||
);
|
||||
const selectedMetadata = pipeline?.context?.selectedMetadata || null;
|
||||
const mediaInfoReview = pipeline?.context?.mediaInfoReview || null;
|
||||
const playlistAnalysis = pipeline?.context?.playlistAnalysis || null;
|
||||
@@ -1789,6 +1805,11 @@ export default function PipelineStatusCard({
|
||||
&& retryJobId
|
||||
&& pipeline?.context?.canRestartEncodeFromLastSettings
|
||||
);
|
||||
const canCancelReview = Boolean(
|
||||
!running
|
||||
&& retryJobId
|
||||
&& (state === 'READY_TO_ENCODE' || state === 'WAITING_FOR_USER_DECISION')
|
||||
);
|
||||
const canRestartReviewFromRaw = Boolean(
|
||||
retryJobId
|
||||
&& !running
|
||||
@@ -2852,7 +2873,7 @@ export default function PipelineStatusCard({
|
||||
/>
|
||||
)}
|
||||
|
||||
{(state === 'METADATA_SELECTION' || state === 'WAITING_FOR_USER_DECISION')
|
||||
{(state === 'METADATA_SELECTION' || state === 'WAITING_FOR_USER_DECISION' || isOrphanCancelled)
|
||||
&& retryJobId
|
||||
&& typeof onOpenMetadata === 'function'
|
||||
&& !(jobMediaProfile === 'converter' && !selectedMetadata?.imdbId) ? (
|
||||
@@ -2882,11 +2903,12 @@ export default function PipelineStatusCard({
|
||||
&& state !== 'DISC_DETECTED'
|
||||
&& retryJobId
|
||||
&& typeof onReassignOmdb === 'function'
|
||||
&& !isOrphanCancelled
|
||||
&& !(jobMediaProfile === 'converter' && state === 'READY_TO_START' && !selectedMetadata?.imdbId) ? (
|
||||
<Button
|
||||
label="OMDb neu zuordnen"
|
||||
label="Metadaten neu zuweisen"
|
||||
icon="pi pi-search"
|
||||
severity="secondary"
|
||||
severity="info"
|
||||
size="small"
|
||||
onClick={() => onReassignOmdb?.(retryJobId)}
|
||||
loading={busy}
|
||||
@@ -2986,7 +3008,7 @@ export default function PipelineStatusCard({
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{running && (
|
||||
{(running || canCancelReview) && (
|
||||
<Button
|
||||
label="Abbrechen"
|
||||
icon="pi pi-stop"
|
||||
@@ -3041,7 +3063,7 @@ export default function PipelineStatusCard({
|
||||
)
|
||||
) : null}
|
||||
|
||||
{(state === 'ERROR' || state === 'CANCELLED') && retryJobId && (
|
||||
{(state === 'ERROR' || state === 'CANCELLED') && retryJobId && !isOrphanCancelled && !ripCompleted && (
|
||||
<Button
|
||||
label="Retry Rippen"
|
||||
icon="pi pi-refresh"
|
||||
@@ -3051,7 +3073,7 @@ export default function PipelineStatusCard({
|
||||
/>
|
||||
)}
|
||||
|
||||
{(state === 'ERROR' || state === 'CANCELLED') ? (
|
||||
{(state === 'ERROR' || state === 'CANCELLED') && !isOrphanCancelled && !ripCompleted ? (
|
||||
<Button
|
||||
label="Disk-Analyse neu starten"
|
||||
icon="pi pi-search"
|
||||
@@ -3062,7 +3084,7 @@ export default function PipelineStatusCard({
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
{retryJobId && typeof onDeleteJob === 'function' ? (
|
||||
{!running && retryJobId && typeof onDeleteJob === 'function' ? (
|
||||
<Button
|
||||
label="Job löschen"
|
||||
icon="pi pi-trash"
|
||||
|
||||
@@ -1279,6 +1279,7 @@ function ConverterVideoJobCard({
|
||||
|
||||
<PipelineStatusCard
|
||||
jobId={jobId}
|
||||
jobRow={job}
|
||||
pipeline={pipeline}
|
||||
onOpenMetadata={onOpenMetadata}
|
||||
onReassignOmdb={onReassignOmdb}
|
||||
|
||||
@@ -76,10 +76,9 @@ export default function DatabasePage() {
|
||||
: (newJobId ? `Historieneintrag #${newJobId} erstellt.` : 'Historieneintrag wurde erstellt.'),
|
||||
life: 3500
|
||||
});
|
||||
// Jump to the Ripper view immediately after creating a job from /database.
|
||||
navigate('/ripper');
|
||||
await loadOrphans();
|
||||
if (activationStarted) {
|
||||
navigate('/ripper');
|
||||
}
|
||||
} catch (error) {
|
||||
toastRef.current?.show({
|
||||
severity: 'error',
|
||||
|
||||
@@ -64,6 +64,20 @@ function normalizeDrivePath(value) {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function getPathBaseName(value) {
|
||||
const normalized = String(value || '').trim().replace(/[\\]+/g, '/');
|
||||
if (!normalized) {
|
||||
return '';
|
||||
}
|
||||
const segments = normalized.split('/').filter(Boolean);
|
||||
return segments[segments.length - 1] || '';
|
||||
}
|
||||
|
||||
function isIncompleteRawPath(rawPath) {
|
||||
const baseName = getPathBaseName(rawPath);
|
||||
return /^incomplete_/i.test(baseName);
|
||||
}
|
||||
|
||||
function formatPercent(value, digits = 1) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
@@ -1643,27 +1657,7 @@ export default function RipperPage({
|
||||
latestCancelledJob = await fetchLatestCancelledJob();
|
||||
}
|
||||
}
|
||||
const latestStatus = String(
|
||||
latestCancelledJob?.status
|
||||
|| latestCancelledJob?.last_state
|
||||
|| ''
|
||||
).trim().toUpperCase();
|
||||
const autoPreparedForRestart = cancelledState === 'ENCODING' && latestStatus === 'READY_TO_ENCODE';
|
||||
if (cancelledState === 'ENCODING' && cancelledJobId && !autoPreparedForRestart) {
|
||||
setCancelCleanupDialog({
|
||||
visible: true,
|
||||
jobId: cancelledJobId,
|
||||
target: 'movie',
|
||||
path: latestCancelledJob?.output_path || cancelledJob?.output_path || null
|
||||
});
|
||||
} else if (cancelledState === 'RIPPING' && cancelledJobId) {
|
||||
setCancelCleanupDialog({
|
||||
visible: true,
|
||||
jobId: cancelledJobId,
|
||||
target: 'raw',
|
||||
path: latestCancelledJob?.raw_path || cancelledJob?.raw_path || null
|
||||
});
|
||||
}
|
||||
// No post-cancel cleanup dialog here; encode/rip cleanup is handled by settings or retry flows.
|
||||
} catch (error) {
|
||||
showError(error);
|
||||
} finally {
|
||||
@@ -1939,13 +1933,20 @@ export default function RipperPage({
|
||||
return;
|
||||
}
|
||||
const job = ripperJobs.find((item) => normalizeJobId(item?.id) === normalizedJobId) || null;
|
||||
const ripSuccessful = Number(job?.rip_successful || 0) === 1;
|
||||
const deleteIncompleteRaw = !ripSuccessful && isIncompleteRawPath(job?.raw_path);
|
||||
const deleteTarget = deleteIncompleteRaw ? 'raw' : 'none';
|
||||
const title = String(job?.title || job?.detected_title || `Job #${normalizedJobId}`).trim();
|
||||
const confirmed = await confirmModal({
|
||||
header: 'Job löschen',
|
||||
message:
|
||||
`Job #${normalizedJobId} wirklich löschen?\n` +
|
||||
`${title}\n\n` +
|
||||
'Hinweis: Dateien bleiben erhalten. Das zugeordnete Laufwerk wird freigegeben und auf Leerzustand zurückgesetzt.',
|
||||
(deleteIncompleteRaw
|
||||
? 'Hinweis: Unvollständiges RAW wird gelöscht. Für einen neuen Versuch bitte das Laufwerk erneut analysieren.'
|
||||
: 'Hinweis: Dateien bleiben erhalten. Vollständiges RAW ist anschließend über /database wiederherstellbar.') +
|
||||
'\nZugehörige Einträge (z.B. Serien-Container oder Folgejobs) werden ebenfalls entfernt.' +
|
||||
'\nDas zugeordnete Laufwerk wird freigegeben und auf Leerzustand zurückgesetzt.',
|
||||
acceptLabel: 'Löschen',
|
||||
rejectLabel: 'Abbrechen',
|
||||
danger: true
|
||||
@@ -1969,8 +1970,8 @@ export default function RipperPage({
|
||||
await wait(250);
|
||||
}
|
||||
}
|
||||
await api.deleteJobEntry(normalizedJobId, 'none', {
|
||||
includeRelated: false,
|
||||
await api.deleteJobEntry(normalizedJobId, deleteTarget, {
|
||||
includeRelated: true,
|
||||
resetDriveState: true,
|
||||
keepDetectedDevice: false
|
||||
});
|
||||
@@ -2207,7 +2208,40 @@ export default function RipperPage({
|
||||
setBusy(true);
|
||||
try {
|
||||
if (metadataDialogReassignMode) {
|
||||
await api.assignJobOmdb(payload.jobId, payload);
|
||||
const providerRaw = String(payload?.metadataProvider || '').trim().toLowerCase();
|
||||
const nextWorkflowKind = String(
|
||||
payload?.workflowKind
|
||||
|| (providerRaw === 'tmdb' ? 'series' : (providerRaw === 'omdb' ? 'film' : ''))
|
||||
|| ''
|
||||
).trim().toLowerCase();
|
||||
const reassignJob = ripperJobs.find((item) => normalizeJobId(item?.id) === normalizeJobId(payload?.jobId)) || null;
|
||||
const currentWorkflowKind = String(
|
||||
reassignJob?.makemkvInfo?.analyzeContext?.selectedMetadata?.workflowKind
|
||||
|| reassignJob?.makemkvInfo?.analyzeContext?.workflowKind
|
||||
|| reassignJob?.makemkvInfo?.selectedMetadata?.workflowKind
|
||||
|| effectiveMetadataDialogContext?.selectedMetadata?.workflowKind
|
||||
|| ''
|
||||
).trim().toLowerCase();
|
||||
const currentProvider = String(
|
||||
reassignJob?.makemkvInfo?.analyzeContext?.metadataProvider
|
||||
|| reassignJob?.makemkvInfo?.selectedMetadata?.metadataProvider
|
||||
|| effectiveMetadataDialogContext?.metadataProvider
|
||||
|| ''
|
||||
).trim().toLowerCase();
|
||||
const shouldRestartReview = Boolean(
|
||||
nextWorkflowKind
|
||||
&& currentWorkflowKind
|
||||
&& nextWorkflowKind !== currentWorkflowKind
|
||||
) || Boolean(
|
||||
currentProvider
|
||||
&& providerRaw
|
||||
&& currentProvider !== providerRaw
|
||||
);
|
||||
if (shouldRestartReview) {
|
||||
await api.selectMetadata(payload);
|
||||
} else {
|
||||
await api.assignJobOmdb(payload.jobId, payload);
|
||||
}
|
||||
} else {
|
||||
await api.selectMetadata(payload);
|
||||
}
|
||||
@@ -3051,6 +3085,7 @@ export default function RipperPage({
|
||||
{!isCdJob && !isAudiobookJob ? (
|
||||
<PipelineStatusCard
|
||||
jobId={jobId}
|
||||
jobRow={job}
|
||||
pipeline={pipelineForJob}
|
||||
onAnalyze={handleAnalyze}
|
||||
onReanalyze={handleReanalyze}
|
||||
|
||||
Reference in New Issue
Block a user