0.13.1-7 Fix Series

This commit is contained in:
2026-04-14 13:28:03 +00:00
parent 9297a84eea
commit c89a93ed05
17 changed files with 174 additions and 63 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.13.1-6", "version": "0.13.1-7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.13.1-6", "version": "0.13.1-7",
"dependencies": { "dependencies": {
"archiver": "^7.0.1", "archiver": "^7.0.1",
"cors": "^2.8.5", "cors": "^2.8.5",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ripster-backend", "name": "ripster-backend",
"version": "0.13.1-6", "version": "0.13.1-7",
"private": true, "private": true,
"type": "commonjs", "type": "commonjs",
"scripts": { "scripts": {
+61 -13
View File
@@ -2356,6 +2356,14 @@ function normalizeJobIdValue(value) {
return Math.trunc(parsed); return Math.trunc(parsed);
} }
function isSeriesContainerRow(row) {
return String(row?.job_kind || '').trim().toLowerCase() === 'dvd_series_container';
}
function isSeriesChildRow(row) {
return String(row?.job_kind || '').trim().toLowerCase() === 'dvd_series_child';
}
function normalizeArchiveTarget(value) { function normalizeArchiveTarget(value) {
const raw = String(value || '').trim().toLowerCase(); const raw = String(value || '').trim().toLowerCase();
if (raw === 'raw') { if (raw === 'raw') {
@@ -4771,6 +4779,11 @@ class HistoryService {
if (!entry.isDirectory() || isHiddenDirectoryName(entry.name)) { if (!entry.isDirectory() || isHiddenDirectoryName(entry.name)) {
continue; continue;
} }
if (/^incomplete_/i.test(String(entry.name || '').trim())) {
// Incomplete RAW folders represent unfinished rips and are intentionally
// not re-importable from /database.
continue;
}
const rawPath = path.join(rawDir, entry.name); const rawPath = path.join(rawDir, entry.name);
const normalizedPath = normalizeComparablePath(rawPath); const normalizedPath = normalizeComparablePath(rawPath);
@@ -5765,6 +5778,12 @@ class HistoryService {
const pending = [normalizedJobId]; const pending = [normalizedJobId];
const visited = new Set(); const visited = new Set();
const parentRow = normalizeJobIdValue(primary?.parent_job_id) !== null
? byId.get(normalizeJobIdValue(primary?.parent_job_id))
: null;
const isPrimarySeriesChild = isSeriesChildRow(primary)
|| (parentRow && isSeriesContainerRow(parentRow));
const isPrimarySeriesContainer = isSeriesContainerRow(primary);
const enqueue = (value) => { const enqueue = (value) => {
const id = normalizeJobIdValue(value); const id = normalizeJobIdValue(value);
if (!id || visited.has(id)) { if (!id || visited.has(id)) {
@@ -5785,8 +5804,10 @@ class HistoryService {
continue; continue;
} }
if (!(isPrimarySeriesChild || isPrimarySeriesContainer)) {
enqueue(row.parent_job_id); enqueue(row.parent_job_id);
enqueue(parseSourceJobIdFromPlan(row.encode_plan_json)); enqueue(parseSourceJobIdFromPlan(row.encode_plan_json));
}
for (const childId of (childrenByParent.get(currentId) || [])) { for (const childId of (childrenByParent.get(currentId) || [])) {
enqueue(childId); enqueue(childId);
@@ -5795,7 +5816,7 @@ class HistoryService {
enqueue(childId); enqueue(childId);
} }
if (includeLogLinks) { if (includeLogLinks && !(isPrimarySeriesChild || isPrimarySeriesContainer)) {
try { try {
const processLog = await this.readProcessLogLines(currentId, { includeAll: true }); const processLog = await this.readProcessLogLines(currentId, { includeAll: true });
const linkedJobIds = parseRetryLinkedJobIdsFromLogLines(processLog.lines); const linkedJobIds = parseRetryLinkedJobIdsFromLogLines(processLog.lines);
@@ -5808,6 +5829,25 @@ class HistoryService {
} }
} }
if (isPrimarySeriesChild && parentRow && isSeriesContainerRow(parentRow)) {
const parentId = normalizeJobIdValue(parentRow?.id);
if (parentId) {
const remainingChildren = rows.filter((row) => {
const rowId = normalizeJobIdValue(row?.id);
if (!rowId) {
return false;
}
if (normalizeJobIdValue(row?.parent_job_id) !== parentId) {
return false;
}
return !visited.has(rowId);
});
if (remainingChildren.length === 0) {
visited.add(parentId);
}
}
}
return Array.from(visited) return Array.from(visited)
.map((id) => byId.get(id)) .map((id) => byId.get(id))
.filter(Boolean) .filter(Boolean)
@@ -6849,16 +6889,23 @@ class HistoryService {
const includeRelated = Boolean(options?.includeRelated); const includeRelated = Boolean(options?.includeRelated);
if (!includeRelated) { if (!includeRelated) {
const existing = await this.getJobById(jobId); const resolved = await this.getJobByIdOrReplacement(jobId);
const existing = resolved?.job || null;
const normalizedJobId = normalizeJobIdValue(resolved?.resolvedJobId || jobId);
if (!existing) { if (!existing) {
const error = new Error('Job nicht gefunden.'); const error = new Error('Job nicht gefunden.');
error.statusCode = 404; error.statusCode = 404;
throw error; throw error;
} }
if (!normalizedJobId) {
const error = new Error('Ungültige Job-ID.');
error.statusCode = 400;
throw error;
}
let fileSummary = null; let fileSummary = null;
if (fileTarget !== 'none') { if (fileTarget !== 'none') {
const preview = await this.getJobDeletePreview(jobId, { includeRelated: false }); const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: false });
fileSummary = this._deletePathsFromPreview(preview, fileTarget, { fileSummary = this._deletePathsFromPreview(preview, fileTarget, {
selectedMoviePaths: options?.selectedMoviePaths selectedMoviePaths: options?.selectedMoviePaths
}); });
@@ -6869,7 +6916,7 @@ class HistoryService {
'SELECT state, active_job_id FROM pipeline_state WHERE id = 1' 'SELECT state, active_job_id FROM pipeline_state WHERE id = 1'
); );
const isActivePipelineJob = Number(pipelineRow?.active_job_id || 0) === Number(jobId); const isActivePipelineJob = Number(pipelineRow?.active_job_id || 0) === Number(normalizedJobId);
const runningStates = new Set(['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_ANALYZING', 'CD_RIPPING', 'CD_ENCODING']); const runningStates = new Set(['ANALYZING', 'RIPPING', 'MEDIAINFO_CHECK', 'ENCODING', 'CD_ANALYZING', 'CD_RIPPING', 'CD_ENCODING']);
if (isActivePipelineJob && runningStates.has(String(pipelineRow?.state || ''))) { if (isActivePipelineJob && runningStates.has(String(pipelineRow?.state || ''))) {
@@ -6904,23 +6951,23 @@ class HistoryService {
updated_at = CURRENT_TIMESTAMP updated_at = CURRENT_TIMESTAMP
WHERE id = 1 AND active_job_id = ? WHERE id = 1 AND active_job_id = ?
`, `,
[jobId] [normalizedJobId]
); );
} }
await db.run('DELETE FROM jobs WHERE id = ?', [jobId]); await db.run('DELETE FROM jobs WHERE id = ?', [normalizedJobId]);
await db.exec('COMMIT'); await db.exec('COMMIT');
} catch (error) { } catch (error) {
await db.exec('ROLLBACK'); await db.exec('ROLLBACK');
throw error; throw error;
} }
await this.closeProcessLog(jobId); await this.closeProcessLog(normalizedJobId);
this._deleteProcessLogFile(jobId); this._deleteProcessLogFile(normalizedJobId);
thumbnailService.deleteThumbnail(jobId); thumbnailService.deleteThumbnail(normalizedJobId);
logger.warn('job:deleted', { logger.warn('job:deleted', {
jobId, jobId: normalizedJobId,
fileTarget, fileTarget,
includeRelated: false, includeRelated: false,
pipelineStateReset: isActivePipelineJob, pipelineStateReset: isActivePipelineJob,
@@ -6934,15 +6981,16 @@ class HistoryService {
return { return {
deleted: true, deleted: true,
jobId, jobId: normalizedJobId,
fileTarget, fileTarget,
includeRelated: false, includeRelated: false,
deletedJobIds: [Number(jobId)], deletedJobIds: [Number(normalizedJobId)],
fileSummary fileSummary
}; };
} }
const normalizedJobId = normalizeJobIdValue(jobId); const resolved = await this.getJobByIdOrReplacement(jobId);
const normalizedJobId = normalizeJobIdValue(resolved?.resolvedJobId || jobId);
const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: true }); const preview = await this.getJobDeletePreview(normalizedJobId, { includeRelated: true });
const deleteJobIds = Array.isArray(preview?.relatedJobs) const deleteJobIds = Array.isArray(preview?.relatedJobs)
? preview.relatedJobs ? preview.relatedJobs
+8 -2
View File
@@ -20984,7 +20984,7 @@ class PipelineService extends EventEmitter {
const shouldAutoRecoverEncode = normalizedStage === 'ENCODING' const shouldAutoRecoverEncode = normalizedStage === 'ENCODING'
&& hasConfirmedPlan && hasConfirmedPlan
&& (!isCancelled || resolvedMediaProfile === 'converter'); && resolvedMediaProfile !== 'audiobook';
if (shouldAutoRecoverEncode) { if (shouldAutoRecoverEncode) {
try { try {
const recoveryReasonLabel = isCancelled ? 'Abbruch' : 'Fehler'; const recoveryReasonLabel = isCancelled ? 'Abbruch' : 'Fehler';
@@ -22782,7 +22782,13 @@ class PipelineService extends EventEmitter {
); );
newRawPath = nextRaw.rawDir; newRawPath = nextRaw.rawDir;
} else { } else {
const rawBaseDir = path.dirname(currentRawPath); const rawStorage = resolveSeriesAwareRawStorage(
settings || {},
mediaProfile,
selectedMetadata,
mkInfo?.analyzeContext || null
);
const rawBaseDir = String(rawStorage?.rawBaseDir || '').trim() || path.dirname(currentRawPath);
const newMetadataBase = buildRawMetadataBase({ const newMetadataBase = buildRawMetadataBase({
title: job.title || job.detected_title || null, title: job.title || job.detected_title || null,
year: job.year || null, year: job.year || null,
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.13.1-6", "version": "0.13.1-7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.13.1-6", "version": "0.13.1-7",
"dependencies": { "dependencies": {
"primeicons": "^7.0.0", "primeicons": "^7.0.0",
"primereact": "^10.9.2", "primereact": "^10.9.2",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ripster-frontend", "name": "ripster-frontend",
"version": "0.13.1-6", "version": "0.13.1-7",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
+2 -2
View File
@@ -940,9 +940,9 @@ function InlineConfig({ job, plan, onStarted, onDeleted, onCancelled, onInputsCh
{isVideo && ( {isVideo && (
<div className="converter-job-card-actions" style={{ marginTop: 0 }}> <div className="converter-job-card-actions" style={{ marginTop: 0 }}>
<Button <Button
label="OMDb neu zuordnen" label="Metadaten neu zuweisen"
icon="pi pi-search" icon="pi pi-search"
severity="secondary" severity="info"
outlined outlined
size="small" size="small"
onClick={() => setMetadataDialogVisible(true)} onClick={() => setMetadataDialogVisible(true)}
+29 -7
View File
@@ -1130,6 +1130,7 @@ export default function PipelineStatusCard({
onRetry, onRetry,
onDeleteJob, onDeleteJob,
onInputsChanged, onInputsChanged,
jobRow,
isQueued = false, isQueued = false,
busy busy
}) { }) {
@@ -1141,6 +1142,21 @@ export default function PipelineStatusCard({
const contextJobId = normalizeJobId(pipeline?.context?.jobId); const contextJobId = normalizeJobId(pipeline?.context?.jobId);
const retryJobId = explicitJobId || contextJobId; const retryJobId = explicitJobId || contextJobId;
const queueLocked = Boolean(isQueued && retryJobId); 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 selectedMetadata = pipeline?.context?.selectedMetadata || null;
const mediaInfoReview = pipeline?.context?.mediaInfoReview || null; const mediaInfoReview = pipeline?.context?.mediaInfoReview || null;
const playlistAnalysis = pipeline?.context?.playlistAnalysis || null; const playlistAnalysis = pipeline?.context?.playlistAnalysis || null;
@@ -1789,6 +1805,11 @@ export default function PipelineStatusCard({
&& retryJobId && retryJobId
&& pipeline?.context?.canRestartEncodeFromLastSettings && pipeline?.context?.canRestartEncodeFromLastSettings
); );
const canCancelReview = Boolean(
!running
&& retryJobId
&& (state === 'READY_TO_ENCODE' || state === 'WAITING_FOR_USER_DECISION')
);
const canRestartReviewFromRaw = Boolean( const canRestartReviewFromRaw = Boolean(
retryJobId retryJobId
&& !running && !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 && retryJobId
&& typeof onOpenMetadata === 'function' && typeof onOpenMetadata === 'function'
&& !(jobMediaProfile === 'converter' && !selectedMetadata?.imdbId) ? ( && !(jobMediaProfile === 'converter' && !selectedMetadata?.imdbId) ? (
@@ -2882,11 +2903,12 @@ export default function PipelineStatusCard({
&& state !== 'DISC_DETECTED' && state !== 'DISC_DETECTED'
&& retryJobId && retryJobId
&& typeof onReassignOmdb === 'function' && typeof onReassignOmdb === 'function'
&& !isOrphanCancelled
&& !(jobMediaProfile === 'converter' && state === 'READY_TO_START' && !selectedMetadata?.imdbId) ? ( && !(jobMediaProfile === 'converter' && state === 'READY_TO_START' && !selectedMetadata?.imdbId) ? (
<Button <Button
label="OMDb neu zuordnen" label="Metadaten neu zuweisen"
icon="pi pi-search" icon="pi pi-search"
severity="secondary" severity="info"
size="small" size="small"
onClick={() => onReassignOmdb?.(retryJobId)} onClick={() => onReassignOmdb?.(retryJobId)}
loading={busy} loading={busy}
@@ -2986,7 +3008,7 @@ export default function PipelineStatusCard({
/> />
) : null} ) : null}
{running && ( {(running || canCancelReview) && (
<Button <Button
label="Abbrechen" label="Abbrechen"
icon="pi pi-stop" icon="pi pi-stop"
@@ -3041,7 +3063,7 @@ export default function PipelineStatusCard({
) )
) : null} ) : null}
{(state === 'ERROR' || state === 'CANCELLED') && retryJobId && ( {(state === 'ERROR' || state === 'CANCELLED') && retryJobId && !isOrphanCancelled && !ripCompleted && (
<Button <Button
label="Retry Rippen" label="Retry Rippen"
icon="pi pi-refresh" icon="pi pi-refresh"
@@ -3051,7 +3073,7 @@ export default function PipelineStatusCard({
/> />
)} )}
{(state === 'ERROR' || state === 'CANCELLED') ? ( {(state === 'ERROR' || state === 'CANCELLED') && !isOrphanCancelled && !ripCompleted ? (
<Button <Button
label="Disk-Analyse neu starten" label="Disk-Analyse neu starten"
icon="pi pi-search" icon="pi pi-search"
@@ -3062,7 +3084,7 @@ export default function PipelineStatusCard({
) : null} ) : null}
</> </>
)} )}
{retryJobId && typeof onDeleteJob === 'function' ? ( {!running && retryJobId && typeof onDeleteJob === 'function' ? (
<Button <Button
label="Job löschen" label="Job löschen"
icon="pi pi-trash" icon="pi pi-trash"
+1
View File
@@ -1279,6 +1279,7 @@ function ConverterVideoJobCard({
<PipelineStatusCard <PipelineStatusCard
jobId={jobId} jobId={jobId}
jobRow={job}
pipeline={pipeline} pipeline={pipeline}
onOpenMetadata={onOpenMetadata} onOpenMetadata={onOpenMetadata}
onReassignOmdb={onReassignOmdb} onReassignOmdb={onReassignOmdb}
+2 -3
View File
@@ -76,10 +76,9 @@ export default function DatabasePage() {
: (newJobId ? `Historieneintrag #${newJobId} erstellt.` : 'Historieneintrag wurde erstellt.'), : (newJobId ? `Historieneintrag #${newJobId} erstellt.` : 'Historieneintrag wurde erstellt.'),
life: 3500 life: 3500
}); });
await loadOrphans(); // Jump to the Ripper view immediately after creating a job from /database.
if (activationStarted) {
navigate('/ripper'); navigate('/ripper');
} await loadOrphans();
} catch (error) { } catch (error) {
toastRef.current?.show({ toastRef.current?.show({
severity: 'error', severity: 'error',
+59 -24
View File
@@ -64,6 +64,20 @@ function normalizeDrivePath(value) {
return String(value || '').trim(); 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) { function formatPercent(value, digits = 1) {
const parsed = Number(value); const parsed = Number(value);
if (!Number.isFinite(parsed)) { if (!Number.isFinite(parsed)) {
@@ -1643,27 +1657,7 @@ export default function RipperPage({
latestCancelledJob = await fetchLatestCancelledJob(); latestCancelledJob = await fetchLatestCancelledJob();
} }
} }
const latestStatus = String( // No post-cancel cleanup dialog here; encode/rip cleanup is handled by settings or retry flows.
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
});
}
} catch (error) { } catch (error) {
showError(error); showError(error);
} finally { } finally {
@@ -1939,13 +1933,20 @@ export default function RipperPage({
return; return;
} }
const job = ripperJobs.find((item) => normalizeJobId(item?.id) === normalizedJobId) || null; 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 title = String(job?.title || job?.detected_title || `Job #${normalizedJobId}`).trim();
const confirmed = await confirmModal({ const confirmed = await confirmModal({
header: 'Job löschen', header: 'Job löschen',
message: message:
`Job #${normalizedJobId} wirklich löschen?\n` + `Job #${normalizedJobId} wirklich löschen?\n` +
`${title}\n\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', acceptLabel: 'Löschen',
rejectLabel: 'Abbrechen', rejectLabel: 'Abbrechen',
danger: true danger: true
@@ -1969,8 +1970,8 @@ export default function RipperPage({
await wait(250); await wait(250);
} }
} }
await api.deleteJobEntry(normalizedJobId, 'none', { await api.deleteJobEntry(normalizedJobId, deleteTarget, {
includeRelated: false, includeRelated: true,
resetDriveState: true, resetDriveState: true,
keepDetectedDevice: false keepDetectedDevice: false
}); });
@@ -2207,7 +2208,40 @@ export default function RipperPage({
setBusy(true); setBusy(true);
try { try {
if (metadataDialogReassignMode) { if (metadataDialogReassignMode) {
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); await api.assignJobOmdb(payload.jobId, payload);
}
} else { } else {
await api.selectMetadata(payload); await api.selectMetadata(payload);
} }
@@ -3051,6 +3085,7 @@ export default function RipperPage({
{!isCdJob && !isAudiobookJob ? ( {!isCdJob && !isAudiobookJob ? (
<PipelineStatusCard <PipelineStatusCard
jobId={jobId} jobId={jobId}
jobRow={job}
pipeline={pipelineForJob} pipeline={pipelineForJob}
onAnalyze={handleAnalyze} onAnalyze={handleAnalyze}
onReanalyze={handleReanalyze} onReanalyze={handleReanalyze}
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "ripster", "name": "ripster",
"version": "0.13.1-6", "version": "0.13.1-7",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ripster", "name": "ripster",
"version": "0.13.1-6", "version": "0.13.1-7",
"devDependencies": { "devDependencies": {
"concurrently": "^9.1.2" "concurrently": "^9.1.2"
} }
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "ripster", "name": "ripster",
"private": true, "private": true,
"version": "0.13.1-6", "version": "0.13.1-7",
"scripts": { "scripts": {
"dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"",
"dev:backend": "npm run dev --prefix backend", "dev:backend": "npm run dev --prefix backend",