From 56f813358982410be912ef5799002595d931f017 Mon Sep 17 00:00:00 2001 From: mboehmlaender Date: Mon, 23 Mar 2026 08:55:36 +0000 Subject: [PATCH] 0.12.0-12 Job Fix --- .claude/settings.json | 4 +- backend/package-lock.json | 4 +- backend/package.json | 2 +- backend/src/services/pipelineService.js | 101 ++++++++++++++++-------- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/src/pages/DashboardPage.jsx | 13 ++- package-lock.json | 4 +- package.json | 2 +- 9 files changed, 93 insertions(+), 43 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index 1fec878..a622031 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -24,7 +24,9 @@ "Bash(lsblk -o NAME,TYPE,MODEL)", "Bash(node --check /home/michael/ripster/backend/src/routes/pipelineRoutes.js)", "Bash(grep -n \"inferMediaProfile\" /home/michael/ripster/backend/src/services/*.js)", - "Bash(grep -rn \"PROCESS_LOG\\\\|LOG_UPDATE\\\\|processLog\" /home/michael/ripster/backend/src --include=*.js)" + "Bash(grep -rn \"PROCESS_LOG\\\\|LOG_UPDATE\\\\|processLog\" /home/michael/ripster/backend/src --include=*.js)", + "Bash(grep -n \"buildHandBrakeConfig\" /home/michael/ripster/backend/src/services/*.js)", + "Bash(grep -n \"class.*Plugin\\\\|get id\\(\\)\\\\|get name\\(\\)\" /home/michael/ripster/backend/src/plugins/*.js)" ] } } diff --git a/backend/package-lock.json b/backend/package-lock.json index 8bb0216..ea2ab68 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.12.0-11", + "version": "0.12.0-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.12.0-11", + "version": "0.12.0-12", "dependencies": { "archiver": "^7.0.1", "cors": "^2.8.5", diff --git a/backend/package.json b/backend/package.json index 277d0fd..e8a48c8 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.12.0-11", + "version": "0.12.0-12", "private": true, "type": "commonjs", "scripts": { diff --git a/backend/src/services/pipelineService.js b/backend/src/services/pipelineService.js index 7ac1029..2585437 100644 --- a/backend/src/services/pipelineService.js +++ b/backend/src/services/pipelineService.js @@ -8187,17 +8187,18 @@ class PipelineService extends EventEmitter { } const hasEncodableTitle = Boolean(review.encodeInputPath && review.encodeInputTitleId); + const readyStatusText = review.titleSelectionRequired + ? 'Titel-/Spurprüfung fertig - Titel per Checkbox wählen' + : (hasEncodableTitle + ? 'Titel-/Spurprüfung fertig - Auswahl bestätigen, dann Encode manuell starten' + : 'Titel-/Spurprüfung fertig - kein Titel erfüllt MIN_LENGTH_MINUTES'); if (this.isPrimaryJob(jobId)) { const mergedPluginExecutionForReady = getMergedReviewPluginExecution(); await this.setState('READY_TO_ENCODE', { activeJobId: jobId, progress: 0, eta: null, - statusText: review.titleSelectionRequired - ? 'Titel-/Spurprüfung fertig - Titel per Checkbox wählen' - : (hasEncodableTitle - ? 'Titel-/Spurprüfung fertig - Auswahl bestätigen, dann Encode manuell starten' - : 'Titel-/Spurprüfung fertig - kein Titel erfüllt MIN_LENGTH_MINUTES'), + statusText: readyStatusText, context: { ...(this.snapshot.context || {}), jobId, @@ -8218,6 +8219,26 @@ class PipelineService extends EventEmitter { ...(mergedPluginExecutionForReady ? { pluginExecution: mergedPluginExecutionForReady } : {}) } }); + } else { + await this.updateProgress('READY_TO_ENCODE', 0, null, readyStatusText, jobId, { + contextPatch: { + jobId, + rawPath, + inputPath: review.encodeInputPath || null, + hasEncodableTitle, + reviewConfirmed: false, + mode, + mediaProfile, + sourceJobId: options.sourceJobId || null, + mediaInfoReview: review, + selectedMetadata, + playlistAnalysis: playlistAnalysis || null, + playlistDecisionRequired, + playlistCandidates, + selectedPlaylist: resolvedPlaylistId || null, + selectedTitleId: selectedTitleForReview + } + }); } void this.notifyPushover('metadata_ready', { @@ -10164,27 +10185,35 @@ class PipelineService extends EventEmitter { 'SYSTEM', `HandBrake DVD Titelauswahl erforderlich: ${titleCandidates.length} Kandidaten. Nutzer muss Titel wählen.` ); + const dvdWaitingStatusText = `DVD Titelauswahl: ${titleCandidates.length} Kandidaten gefunden`; + const dvdWaitingContextPatch = { + jobId, + rawPath, + handBrakeTitleDecisionRequired: true, + handBrakeTitleCandidates: candidatesData, + handBrakeDvdInputPath: dvdScanInputPath, + reviewConfirmed: false, + mode: options.mode || 'rip', + mediaProfile, + sourceJobId: options.sourceJobId || null, + mediaInfoReview: enrichedReview, + selectedMetadata + }; if (this.isPrimaryJob(jobId)) { await this.setState('WAITING_FOR_USER_DECISION', { activeJobId: jobId, progress: 0, eta: null, - statusText: `DVD Titelauswahl: ${titleCandidates.length} Kandidaten gefunden`, + statusText: dvdWaitingStatusText, context: { - jobId, - rawPath, - handBrakeTitleDecisionRequired: true, - handBrakeTitleCandidates: candidatesData, - handBrakeDvdInputPath: dvdScanInputPath, - reviewConfirmed: false, - mode: options.mode || 'rip', - mediaProfile, - sourceJobId: options.sourceJobId || null, - mediaInfoReview: enrichedReview, - selectedMetadata, + ...dvdWaitingContextPatch, ...(reviewPluginExecution ? { pluginExecution: this.mergePluginExecutionState(mkInfo?.pluginExecution, reviewPluginExecution) } : {}) } }); + } else { + await this.updateProgress('WAITING_FOR_USER_DECISION', 0, null, dvdWaitingStatusText, jobId, { + contextPatch: dvdWaitingContextPatch + }); } void this.notifyPushover('metadata_ready', { title: 'Ripster - DVD Titelauswahl', @@ -10247,30 +10276,38 @@ class PipelineService extends EventEmitter { ); } + const mediainfoReadyStatusText = titleSelectionRequired + ? 'Mediainfo geprüft - Titelauswahl per Checkbox erforderlich' + : (hasEncodableTitle + ? 'Mediainfo geprüft - Encode manuell starten' + : 'Mediainfo geprüft - kein Titel erfüllt MIN_LENGTH_MINUTES'); + const mediainfoReadyContextPatch = { + jobId, + rawPath, + inputPath: enrichedReview.encodeInputPath || null, + hasEncodableTitle, + reviewConfirmed: false, + mode: options.mode || 'rip', + mediaProfile, + sourceJobId: options.sourceJobId || null, + mediaInfoReview: enrichedReview, + selectedMetadata + }; if (this.isPrimaryJob(jobId)) { await this.setState('READY_TO_ENCODE', { activeJobId: jobId, progress: 0, eta: null, - statusText: titleSelectionRequired - ? 'Mediainfo geprüft - Titelauswahl per Checkbox erforderlich' - : (hasEncodableTitle - ? 'Mediainfo geprüft - Encode manuell starten' - : 'Mediainfo geprüft - kein Titel erfüllt MIN_LENGTH_MINUTES'), + statusText: mediainfoReadyStatusText, context: { - jobId, - rawPath, - inputPath: enrichedReview.encodeInputPath || null, - hasEncodableTitle, - reviewConfirmed: false, - mode: options.mode || 'rip', - mediaProfile, - sourceJobId: options.sourceJobId || null, - mediaInfoReview: enrichedReview, - selectedMetadata, + ...mediainfoReadyContextPatch, ...(reviewPluginExecution ? { pluginExecution: this.mergePluginExecutionState(mkInfo?.pluginExecution, reviewPluginExecution) } : {}) } }); + } else { + await this.updateProgress('READY_TO_ENCODE', 0, null, mediainfoReadyStatusText, jobId, { + contextPatch: mediainfoReadyContextPatch + }); } void this.notifyPushover('metadata_ready', { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bfbf49e..3749297 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.12.0-11", + "version": "0.12.0-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.12.0-11", + "version": "0.12.0-12", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index 3f64087..fc1e362 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.12.0-11", + "version": "0.12.0-12", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/pages/DashboardPage.jsx b/frontend/src/pages/DashboardPage.jsx index 99df44a..fcdd0e3 100644 --- a/frontend/src/pages/DashboardPage.jsx +++ b/frontend/src/pages/DashboardPage.jsx @@ -981,6 +981,17 @@ export default function DashboardPage({ () => buildCdDriveLifecycleKey(pipeline?.cdDrives), [pipeline?.cdDrives] ); + // Detects when a background job reaches a terminal interactive state via PIPELINE_PROGRESS + // (e.g. READY_TO_ENCODE or WAITING_FOR_USER_DECISION), triggering a dashboard jobs reload. + const jobProgressInteractiveKey = useMemo(() => Object.entries(pipeline?.jobProgress || {}) + .filter(([, v]) => { + const s = String(v?.state || '').toUpperCase(); + return s === 'READY_TO_ENCODE' || s === 'WAITING_FOR_USER_DECISION'; + }) + .map(([id]) => id) + .sort() + .join(','), + [pipeline?.jobProgress]); const monitoringSample = monitoringState.sample; const cpuMetrics = monitoringSample?.cpu || null; const memoryMetrics = monitoringSample?.memory || null; @@ -1215,7 +1226,7 @@ export default function DashboardPage({ useEffect(() => { void loadDashboardJobs(); - }, [pipeline?.state, pipeline?.activeJobId, pipeline?.context?.jobId, cdDriveLifecycleKey, jobsRefreshToken]); + }, [pipeline?.state, pipeline?.activeJobId, pipeline?.context?.jobId, cdDriveLifecycleKey, jobsRefreshToken, jobProgressInteractiveKey]); useEffect(() => { api.getDetectedDrives() diff --git a/package-lock.json b/package-lock.json index 78158c7..d518781 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster", - "version": "0.12.0-11", + "version": "0.12.0-12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster", - "version": "0.12.0-11", + "version": "0.12.0-12", "devDependencies": { "concurrently": "^9.1.2" } diff --git a/package.json b/package.json index cacd137..65e9814 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ripster", "private": true, - "version": "0.12.0-11", + "version": "0.12.0-12", "scripts": { "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev:backend": "npm run dev --prefix backend",