diff --git a/backend/package-lock.json b/backend/package-lock.json index 3f4607d..323dca8 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-backend", - "version": "0.13.1-2", + "version": "0.13.1-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-backend", - "version": "0.13.1-2", + "version": "0.13.1-3", "dependencies": { "archiver": "^7.0.1", "cors": "^2.8.5", diff --git a/backend/package.json b/backend/package.json index 398c2ad..348bc81 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-backend", - "version": "0.13.1-2", + "version": "0.13.1-3", "private": true, "type": "commonjs", "scripts": { diff --git a/backend/src/db/database.js b/backend/src/db/database.js index ab9751c..244adec 100644 --- a/backend/src/db/database.js +++ b/backend/src/db/database.js @@ -807,7 +807,9 @@ async function removeDeprecatedSettings(db) { 'use_plugin_architecture_cd_rip', 'use_plugin_architecture_audiobook', 'use_plugin_architecture_audiobook_analyze', - 'use_plugin_architecture_audiobook_encode' + 'use_plugin_architecture_audiobook_encode', + 'dvd_series_plugin_enabled', + 'dvd_series_prescan_enabled' ]; for (const key of deprecatedKeys) { const schemaResult = await db.run('DELETE FROM settings_schema WHERE key = ?', [key]); @@ -1032,18 +1034,6 @@ async function migrateSettingsSchemaMetadata(db) { ); await db.run(`INSERT OR IGNORE INTO settings_values (key, value) VALUES ('series_dir_dvd_owner', NULL)`); - await db.run( - `INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index) - VALUES ('dvd_series_plugin_enabled', 'Features', 'DVD Serien-Plugin aktiviert', 'boolean', 1, 'Aktiviert die additive Serienerkennung für DVDs. Wenn deaktiviert, bleibt der bestehende DVD-Film-Flow unverändert.', 'false', '[]', '{}', 145)` - ); - await db.run(`INSERT OR IGNORE INTO settings_values (key, value) VALUES ('dvd_series_plugin_enabled', 'false')`); - - await db.run( - `INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index, depends_on) - VALUES ('dvd_series_prescan_enabled', 'Features', 'DVD HandBrake-Prescan aktiviert', 'boolean', 1, 'Führt nach der DVD-Erkennung einen zusätzlichen HandBrake-Prescan für die Serienheuristik aus.', 'true', '[]', '{}', 146, 'dvd_series_plugin_enabled')` - ); - await db.run(`INSERT OR IGNORE INTO settings_values (key, value) VALUES ('dvd_series_prescan_enabled', 'true')`); - await db.run( `INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index) VALUES ('output_template_dvd_series_episode', 'Pfade', 'Output Template (DVD Serie, Episode)', 'string', 1, 'Template für einzelne DVD-Serienepisoden ohne Dateiendung. Platzhalter: {seriesTitle}, {seasonNr}, {episodeNr}, {episodeTitle}, {discNr}, {year}, {language}. Optional mit führender Null: {0:seasonNr}, {0:episodeNr}. Alias: {seasonNo}/{episodeNo} entspricht {seasonNr}/{episodeNr}.', '{seriesTitle}/Staffel {seasonNr}/S{seasonNr}E{episodeNr} - {episodeTitle}', '[]', '{"minLength":1}', 536)` diff --git a/backend/src/plugins/DVDSeriesPlugin.js b/backend/src/plugins/DVDSeriesPlugin.js index c204344..85f74a5 100644 --- a/backend/src/plugins/DVDSeriesPlugin.js +++ b/backend/src/plugins/DVDSeriesPlugin.js @@ -86,18 +86,6 @@ class DVDSeriesPlugin extends SourcePlugin { getSettingsSchema() { return [ - { - key: 'dvd_series_plugin_enabled', - category: 'Features', - label: 'DVD Serien-Plugin aktiviert', - type: 'boolean', - required: 1, - description: 'Aktiviert die additive Serienerkennung für DVDs.', - default_value: 'false', - options_json: '[]', - validation_json: '{}', - order_index: 145 - }, { key: 'tmdb_api_read_access_token', category: 'Metadaten', diff --git a/backend/src/services/pipelineService.js b/backend/src/services/pipelineService.js index ed22886..fe23166 100644 --- a/backend/src/services/pipelineService.js +++ b/backend/src/services/pipelineService.js @@ -11740,97 +11740,88 @@ class PipelineService extends EventEmitter { if (mediaProfile === 'dvd' && analyzePlugin?.id === 'dvd') { try { const dvdSettings = await settingsService.getEffectiveSettingsMap('dvd'); - const dvdSeriesPluginEnabled = ['1', 'true', 'yes', 'on'].includes( - String(dvdSettings?.dvd_series_plugin_enabled || '').trim().toLowerCase() - ); - const dvdSeriesPrescanEnabled = ['1', 'true', 'yes', 'on'].includes( - String(dvdSettings?.dvd_series_prescan_enabled || '').trim().toLowerCase() - ); + const reviewResult = await this.runPluginReviewScan(analyzePlugin, job, { + jobId: job.id, + deviceInfo: deviceWithProfile, + reviewMode: 'pre_rip', + source: 'HANDBRAKE_SCAN', + silent: true + }); + pluginExecution = this.mergePluginExecutionState(pluginExecution, reviewResult?.pluginExecution || null); - if (dvdSeriesPluginEnabled && dvdSeriesPrescanEnabled) { - const reviewResult = await this.runPluginReviewScan(analyzePlugin, job, { + const scanText = Array.isArray(reviewResult?.scanLines) + ? reviewResult.scanLines.join('\n') + : ''; + if (scanText) { + const { DVDSeriesPlugin } = require('../plugins/DVDSeriesPlugin'); + const dvdSeriesPlugin = new DVDSeriesPlugin(); + const dvdSeriesContext = await this.buildPluginContext(dvdSeriesPlugin.id, { + discInfo: deviceWithProfile, jobId: job.id, - deviceInfo: deviceWithProfile, - reviewMode: 'pre_rip', - source: 'HANDBRAKE_SCAN', - silent: true + scanText, + detectedTitle: effectiveDetectedTitle }); - pluginExecution = this.mergePluginExecutionState(pluginExecution, reviewResult?.pluginExecution || null); + const seriesPluginResult = await dvdSeriesPlugin.analyze(device.path, job, dvdSeriesContext); + pluginExecution = this.mergePluginExecutionState( + pluginExecution, + this.sanitizePluginExecutionState(dvdSeriesContext.getPluginExecution()) + ); - const scanText = Array.isArray(reviewResult?.scanLines) - ? reviewResult.scanLines.join('\n') - : ''; - if (scanText) { - const { DVDSeriesPlugin } = require('../plugins/DVDSeriesPlugin'); - const dvdSeriesPlugin = new DVDSeriesPlugin(); - const dvdSeriesContext = await this.buildPluginContext(dvdSeriesPlugin.id, { - discInfo: deviceWithProfile, - jobId: job.id, - scanText, - detectedTitle: effectiveDetectedTitle - }); - const seriesPluginResult = await dvdSeriesPlugin.analyze(device.path, job, dvdSeriesContext); - pluginExecution = this.mergePluginExecutionState( - pluginExecution, - this.sanitizePluginExecutionState(dvdSeriesContext.getPluginExecution()) - ); - - seriesAnalysis = seriesPluginResult?.seriesAnalysis || null; - seriesLookupHint = seriesPluginResult?.seriesLookupHint || null; - const rawSeriesSummary = seriesAnalysis?.summary && typeof seriesAnalysis.summary === 'object' - ? seriesAnalysis.summary - : null; - const seriesLike = Boolean(rawSeriesSummary?.seriesLike); - seriesAnalysis = rawSeriesSummary - ? { - source: String(seriesAnalysis?.source || 'handbrake_scan_text').trim() || 'handbrake_scan_text', - generatedAt: seriesAnalysis?.generatedAt || nowIso(), - summary: { - seriesLike, - confidence: String(rawSeriesSummary?.confidence || 'low').trim().toLowerCase() || 'low', - reasons: Array.isArray(rawSeriesSummary?.reasons) ? rawSeriesSummary.reasons : [], - titleCount: Number(rawSeriesSummary?.titleCount || 0) || 0 - } + seriesAnalysis = seriesPluginResult?.seriesAnalysis || null; + seriesLookupHint = seriesPluginResult?.seriesLookupHint || null; + const rawSeriesSummary = seriesAnalysis?.summary && typeof seriesAnalysis.summary === 'object' + ? seriesAnalysis.summary + : null; + const seriesLike = Boolean(rawSeriesSummary?.seriesLike); + seriesAnalysis = rawSeriesSummary + ? { + source: String(seriesAnalysis?.source || 'handbrake_scan_text').trim() || 'handbrake_scan_text', + generatedAt: seriesAnalysis?.generatedAt || nowIso(), + summary: { + seriesLike, + confidence: String(rawSeriesSummary?.confidence || 'low').trim().toLowerCase() || 'low', + reasons: Array.isArray(rawSeriesSummary?.reasons) ? rawSeriesSummary.reasons : [], + titleCount: Number(rawSeriesSummary?.titleCount || 0) || 0 } - : null; - - if (seriesLike) { - metadataProvider = 'tmdb'; - metadataCandidates = []; - const fallbackQuery = String(seriesLookupHint?.query || effectiveDetectedTitle || '').trim() || null; - if (fallbackQuery) { - seriesLookupHint = { - ...(seriesLookupHint && typeof seriesLookupHint === 'object' ? seriesLookupHint : {}), - query: fallbackQuery, - seriesTitle: String(seriesLookupHint?.seriesTitle || fallbackQuery).trim() || fallbackQuery - }; } + : null; - if (seriesPluginResult?.providerConfigured && seriesLookupHint?.query) { - metadataCandidates = await tmdbService.searchSeriesWithSeasons( - seriesLookupHint.query, - { language: dvdSettings?.dvd_series_language || null } - ).catch((error) => { - logger.warn('dvd-series:tmdb-search-failed', { - jobId: job.id, - query: seriesLookupHint?.query || null, - seasonNumber: seriesLookupHint?.seasonNumber || null, - error: errorToMeta(error) - }); - return []; - }); - } + if (seriesLike) { + metadataProvider = 'tmdb'; + metadataCandidates = []; + const fallbackQuery = String(seriesLookupHint?.query || effectiveDetectedTitle || '').trim() || null; + if (fallbackQuery) { + seriesLookupHint = { + ...(seriesLookupHint && typeof seriesLookupHint === 'object' ? seriesLookupHint : {}), + query: fallbackQuery, + seriesTitle: String(seriesLookupHint?.seriesTitle || fallbackQuery).trim() || fallbackQuery + }; } - logger.info('dvd-series:analyze:done', { - jobId: job.id, - seriesLike: Boolean(seriesAnalysis?.summary?.seriesLike), - confidence: seriesAnalysis?.summary?.confidence || null, - metadataProvider, - metadataCandidateCount: Array.isArray(metadataCandidates) ? metadataCandidates.length : 0, - seriesLookupHint - }); + if (seriesPluginResult?.providerConfigured && seriesLookupHint?.query) { + metadataCandidates = await tmdbService.searchSeriesWithSeasons( + seriesLookupHint.query, + { language: dvdSettings?.dvd_series_language || null } + ).catch((error) => { + logger.warn('dvd-series:tmdb-search-failed', { + jobId: job.id, + query: seriesLookupHint?.query || null, + seasonNumber: seriesLookupHint?.seasonNumber || null, + error: errorToMeta(error) + }); + return []; + }); + } } + + logger.info('dvd-series:analyze:done', { + jobId: job.id, + seriesLike: Boolean(seriesAnalysis?.summary?.seriesLike), + confidence: seriesAnalysis?.summary?.confidence || null, + metadataProvider, + metadataCandidateCount: Array.isArray(metadataCandidates) ? metadataCandidates.length : 0, + seriesLookupHint + }); } } catch (error) { logger.warn('dvd-series:analyze:fallback-legacy', { diff --git a/db/schema.sql b/db/schema.sql index 9e04b9a..eb96e44 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -345,14 +345,6 @@ INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, des VALUES ('hardware_monitoring_interval_ms', 'Monitoring', 'Hardware Monitoring Intervall (ms)', 'number', 1, 'Polling-Intervall für CPU/RAM/GPU/Storage-Metriken.', '5000', '[]', '{"min":1000,"max":60000}', 140); INSERT OR IGNORE INTO settings_values (key, value) VALUES ('hardware_monitoring_interval_ms', '5000'); -INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index) -VALUES ('dvd_series_plugin_enabled', 'Features', 'DVD Serien-Plugin aktiviert', 'boolean', 1, 'Aktiviert die additive Serienerkennung für DVDs. Wenn deaktiviert, bleibt der bestehende DVD-Film-Flow unverändert.', 'false', '[]', '{}', 145); -INSERT OR IGNORE INTO settings_values (key, value) VALUES ('dvd_series_plugin_enabled', 'false'); - -INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index, depends_on) -VALUES ('dvd_series_prescan_enabled', 'Features', 'DVD HandBrake-Prescan aktiviert', 'boolean', 1, 'Führt nach der DVD-Erkennung einen zusätzlichen HandBrake-Prescan für die Serienheuristik aus.', 'true', '[]', '{}', 146, 'dvd_series_plugin_enabled'); -INSERT OR IGNORE INTO settings_values (key, value) VALUES ('dvd_series_prescan_enabled', 'true'); - -- Tools INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index) VALUES ('makemkv_command', 'Tools', 'MakeMKV Kommando', 'string', 1, 'Pfad oder Befehl für makemkvcon.', 'makemkvcon', '[]', '{"minLength":1}', 200); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bad41c8..f6b5046 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster-frontend", - "version": "0.13.1-2", + "version": "0.13.1-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster-frontend", - "version": "0.13.1-2", + "version": "0.13.1-3", "dependencies": { "primeicons": "^7.0.0", "primereact": "^10.9.2", diff --git a/frontend/package.json b/frontend/package.json index 6fa2346..390ebf4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ripster-frontend", - "version": "0.13.1-2", + "version": "0.13.1-3", "private": true, "type": "module", "scripts": { diff --git a/package-lock.json b/package-lock.json index 28ccea5..0f48b4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ripster", - "version": "0.13.1-2", + "version": "0.13.1-3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ripster", - "version": "0.13.1-2", + "version": "0.13.1-3", "devDependencies": { "concurrently": "^9.1.2" } diff --git a/package.json b/package.json index 32035ac..01f783e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ripster", "private": true, - "version": "0.13.1-2", + "version": "0.13.1-3", "scripts": { "dev": "concurrently \"npm run dev --prefix backend\" \"npm run dev --prefix frontend\"", "dev:backend": "npm run dev --prefix backend",