diff --git a/backend/package-lock.json b/backend/package-lock.json index 9448ca7..b67be3d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.9.1", + "version": "0.9.1-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.9.1", + "version": "0.9.1-1", "dependencies": { "cors": "^2.8.5", "dotenv": "^16.4.7", diff --git a/backend/package.json b/backend/package.json index f218b2a..6f8f980 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.9.1", + "version": "0.9.1-1", "private": true, "type": "commonjs", "scripts": { diff --git a/backend/src/routes/historyRoutes.js b/backend/src/routes/historyRoutes.js index 2179ec6..36500b9 100644 --- a/backend/src/routes/historyRoutes.js +++ b/backend/src/routes/historyRoutes.js @@ -91,6 +91,12 @@ router.post( }); const job = await historyService.assignOmdbMetadata(id, payload); + + // Rename raw/output folders to reflect new metadata (best-effort, non-blocking) + pipelineService.renameJobFolders(id).catch((err) => { + logger.warn('post:job:omdb:assign:rename-failed', { id, error: err.message }); + }); + res.json({ job }); }) ); @@ -110,6 +116,12 @@ router.post( }); const job = await historyService.assignCdMetadata(id, payload); + + // Rename raw/output folders to reflect new metadata (best-effort, non-blocking) + pipelineService.renameJobFolders(id).catch((err) => { + logger.warn('post:job:cd:assign:rename-failed', { id, error: err.message }); + }); + res.json({ job }); }) ); diff --git a/backend/src/services/historyService.js b/backend/src/services/historyService.js index 2f7300e..982ac85 100644 --- a/backend/src/services/historyService.js +++ b/backend/src/services/historyService.js @@ -1662,11 +1662,10 @@ class HistoryService { const imdbIdInput = String(payload.imdbId || '').trim().toLowerCase(); let omdb = null; if (imdbIdInput) { - omdb = await omdbService.fetchByImdbId(imdbIdInput); - if (!omdb) { - const error = new Error(`OMDb Eintrag für ${imdbIdInput} nicht gefunden.`); - error.statusCode = 404; - throw error; + try { + omdb = await omdbService.fetchByImdbId(imdbIdInput); + } catch (omdbErr) { + logger.warn('assignOmdbMetadata:fetch-failed', { jobId, imdbId: imdbIdInput, message: omdbErr.message }); } } @@ -1685,7 +1684,7 @@ class HistoryService { const year = Number.isFinite(Number(omdb?.year)) ? Number(omdb.year) : (manualYear !== null ? manualYear : (job.year ?? null)); - const imdbId = omdb?.imdbId || (imdbIdInput || job.imdb_id || null); + const imdbId = omdb?.imdbId || imdbIdInput || job.imdb_id || null; const posterUrl = omdb?.poster || manualPoster || job.poster_url || null; const selectedFromOmdb = omdb ? 1 : Number(payload.fromOmdb ? 1 : 0); diff --git a/backend/src/services/pipelineService.js b/backend/src/services/pipelineService.js index 34f71ba..cdc68f1 100644 --- a/backend/src/services/pipelineService.js +++ b/backend/src/services/pipelineService.js @@ -10766,6 +10766,91 @@ class PipelineService extends EventEmitter { return historyService.getJobById(jobId); } + async renameJobFolders(jobId) { + const job = await historyService.getJobById(jobId); + if (!job) { + return { renamed: [] }; + } + + const renamed = []; + const mediaProfile = this.resolveMediaProfileForJob(job); + const isCd = mediaProfile === 'cd'; + const settings = await settingsService.getEffectiveSettingsMap(mediaProfile); + + // Rename raw folder + const currentRawPath = job.raw_path ? path.resolve(job.raw_path) : null; + if (currentRawPath && fs.existsSync(currentRawPath)) { + const rawBaseDir = path.dirname(currentRawPath); + const newMetadataBase = buildRawMetadataBase({ + title: job.title || job.detected_title || null, + year: job.year || null + }, jobId); + const currentState = resolveRawFolderStateFromPath(currentRawPath); + const newRawDirName = buildRawDirName(newMetadataBase, jobId, { state: currentState }); + const newRawPath = path.join(rawBaseDir, newRawDirName); + + if (normalizeComparablePath(currentRawPath) !== normalizeComparablePath(newRawPath) && !fs.existsSync(newRawPath)) { + try { + fs.renameSync(currentRawPath, newRawPath); + await historyService.updateJob(jobId, { raw_path: newRawPath }); + renamed.push({ type: 'raw', from: currentRawPath, to: newRawPath }); + logger.info('rename-job-folders:raw', { jobId, from: currentRawPath, to: newRawPath }); + } catch (err) { + logger.warn('rename-job-folders:raw-failed', { jobId, error: err.message }); + } + } + } + + // Rename output file (film) or output directory (CD) + const currentOutputPath = job.output_path ? path.resolve(job.output_path) : null; + if (currentOutputPath && fs.existsSync(currentOutputPath)) { + try { + if (isCd) { + const cdInfo = this.safeParseJson(job.makemkv_info_json) || {}; + const selectedMeta = cdInfo.selectedMetadata && typeof cdInfo.selectedMetadata === 'object' + ? cdInfo.selectedMetadata + : {}; + const cdMeta = { + artist: String(selectedMeta.artist || '').trim() || String(job.title || '').trim() || null, + album: String(job.title || selectedMeta.title || '').trim() || null, + year: job.year || selectedMeta.year || null + }; + const cdOutputBaseDir = String(settings.movie_dir || '').trim(); + const cdOutputTemplate = String(settings.cd_output_template || cdRipService.DEFAULT_CD_OUTPUT_TEMPLATE).trim(); + if (cdOutputBaseDir) { + const newCdOutputDir = cdRipService.buildOutputDir(cdMeta, cdOutputBaseDir, cdOutputTemplate); + if (normalizeComparablePath(currentOutputPath) !== normalizeComparablePath(newCdOutputDir) && !fs.existsSync(newCdOutputDir)) { + fs.mkdirSync(path.dirname(newCdOutputDir), { recursive: true }); + fs.renameSync(currentOutputPath, newCdOutputDir); + await historyService.updateJob(jobId, { output_path: newCdOutputDir }); + renamed.push({ type: 'output', from: currentOutputPath, to: newCdOutputDir }); + logger.info('rename-job-folders:cd-output', { jobId, from: currentOutputPath, to: newCdOutputDir }); + } + } + } else { + const newOutputPath = buildFinalOutputPathFromJob(settings, job, jobId); + if (normalizeComparablePath(currentOutputPath) !== normalizeComparablePath(newOutputPath) && !fs.existsSync(newOutputPath)) { + fs.mkdirSync(path.dirname(newOutputPath), { recursive: true }); + moveFileWithFallback(currentOutputPath, newOutputPath); + try { + const oldParentDir = path.dirname(currentOutputPath); + if (fs.readdirSync(oldParentDir).length === 0) { + fs.rmdirSync(oldParentDir); + } + } catch (_ignoreErr) {} + await historyService.updateJob(jobId, { output_path: newOutputPath }); + renamed.push({ type: 'output', from: currentOutputPath, to: newOutputPath }); + logger.info('rename-job-folders:film-output', { jobId, from: currentOutputPath, to: newOutputPath }); + } + } + } catch (err) { + logger.warn('rename-job-folders:output-failed', { jobId, isCd, error: err.message }); + } + } + + return { renamed }; + } + async startCdRip(jobId, ripConfig) { this.ensureNotBusy('startCdRip', jobId); this.cancelRequestedByJob.delete(Number(jobId)); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8f06b96..dbe91c2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.9.1", + "version": "0.9.1-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.9.1", + "version": "0.9.1-1", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index ee85e8f..9daf148 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.9.1", + "version": "0.9.1-1", "private": true, "type": "module", "scripts": { diff --git a/package-lock.json b/package-lock.json index ea168c9..aea1559 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster", - "version": "0.9.1", + "version": "0.9.1-1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster", - "version": "0.9.1", + "version": "0.9.1-1", "devDependencies": { "concurrently": "^9.1.2" } diff --git a/package.json b/package.json index d7edc77..a86a11d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ripster", "private": true, - "version": "0.9.1", + "version": "0.9.1-1", "scripts": { "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev:backend": "npm run dev --prefix backend",