some pload
This commit is contained in:
@@ -3,7 +3,6 @@ const path = require('path');
|
|||||||
const sqlite3 = require('sqlite3');
|
const sqlite3 = require('sqlite3');
|
||||||
const { open } = require('sqlite');
|
const { open } = require('sqlite');
|
||||||
const { dbPath } = require('../config');
|
const { dbPath } = require('../config');
|
||||||
const { defaultSchema } = require('./defaultSettings');
|
|
||||||
const logger = require('../services/logger').child('DB');
|
const logger = require('../services/logger').child('DB');
|
||||||
const { errorToMeta } = require('../utils/errorMeta');
|
const { errorToMeta } = require('../utils/errorMeta');
|
||||||
const { setLogRootDir, getJobLogDir } = require('../services/logPathService');
|
const { setLogRootDir, getJobLogDir } = require('../services/logPathService');
|
||||||
@@ -521,7 +520,7 @@ async function openAndPrepareDatabase() {
|
|||||||
const schemaModel = await loadSchemaModel();
|
const schemaModel = await loadSchemaModel();
|
||||||
await applySchemaModel(dbInstance, schemaModel);
|
await applySchemaModel(dbInstance, schemaModel);
|
||||||
|
|
||||||
await seedDefaultSettings(dbInstance);
|
await seedFromSchemaFile(dbInstance);
|
||||||
await migrateLegacyProfiledToolSettings(dbInstance);
|
await migrateLegacyProfiledToolSettings(dbInstance);
|
||||||
await removeDeprecatedSettings(dbInstance);
|
await removeDeprecatedSettings(dbInstance);
|
||||||
await ensurePipelineStateRow(dbInstance);
|
await ensurePipelineStateRow(dbInstance);
|
||||||
@@ -564,52 +563,16 @@ async function initDatabase({ allowRecovery = true } = {}) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function seedDefaultSettings(db) {
|
async function seedFromSchemaFile(db) {
|
||||||
let seeded = 0;
|
const schemaSql = fs.readFileSync(schemaFilePath, 'utf-8');
|
||||||
for (const item of defaultSchema) {
|
const statements = schemaSql
|
||||||
await db.run(
|
.split(/;\s*\n/)
|
||||||
`
|
.map((s) => s.trim())
|
||||||
INSERT INTO settings_schema
|
.filter((s) => /^INSERT\b/i.test(s));
|
||||||
(key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
for (const stmt of statements) {
|
||||||
VALUES
|
await db.run(stmt);
|
||||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
ON CONFLICT(key) DO UPDATE SET
|
|
||||||
category = excluded.category,
|
|
||||||
label = excluded.label,
|
|
||||||
type = excluded.type,
|
|
||||||
required = excluded.required,
|
|
||||||
description = excluded.description,
|
|
||||||
default_value = COALESCE(settings_schema.default_value, excluded.default_value),
|
|
||||||
options_json = excluded.options_json,
|
|
||||||
validation_json = excluded.validation_json,
|
|
||||||
order_index = excluded.order_index,
|
|
||||||
updated_at = CURRENT_TIMESTAMP
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
item.key,
|
|
||||||
item.category,
|
|
||||||
item.label,
|
|
||||||
item.type,
|
|
||||||
item.required,
|
|
||||||
item.description || null,
|
|
||||||
item.defaultValue || null,
|
|
||||||
JSON.stringify(item.options || []),
|
|
||||||
JSON.stringify(item.validation || {}),
|
|
||||||
item.orderIndex || 0
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
await db.run(
|
|
||||||
`
|
|
||||||
INSERT INTO settings_values (key, value)
|
|
||||||
VALUES (?, ?)
|
|
||||||
ON CONFLICT(key) DO NOTHING
|
|
||||||
`,
|
|
||||||
[item.key, item.defaultValue || null]
|
|
||||||
);
|
|
||||||
seeded += 1;
|
|
||||||
}
|
}
|
||||||
logger.info('seed:settings', { count: seeded });
|
logger.info('seed:settings', { count: statements.length });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readCurrentOrDefaultSettingValue(db, key) {
|
async function readCurrentOrDefaultSettingValue(db, key) {
|
||||||
|
|||||||
@@ -1,709 +0,0 @@
|
|||||||
const defaultSchema = [
|
|
||||||
{
|
|
||||||
key: 'drive_mode',
|
|
||||||
category: 'Laufwerk',
|
|
||||||
label: 'Laufwerksmodus',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'Auto-Discovery oder explizites Device.',
|
|
||||||
defaultValue: 'auto',
|
|
||||||
options: [
|
|
||||||
{ label: 'Auto Discovery', value: 'auto' },
|
|
||||||
{ label: 'Explizites Device', value: 'explicit' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 10
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'drive_device',
|
|
||||||
category: 'Laufwerk',
|
|
||||||
label: 'Device Pfad',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Nur für expliziten Modus, z.B. /dev/sr0.',
|
|
||||||
defaultValue: '/dev/sr0',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 20
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_source_index',
|
|
||||||
category: 'Laufwerk',
|
|
||||||
label: 'MakeMKV Source Index',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Disc Index im Auto-Modus.',
|
|
||||||
defaultValue: '0',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 0, max: 20 },
|
|
||||||
orderIndex: 30
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'disc_poll_interval_ms',
|
|
||||||
category: 'Laufwerk',
|
|
||||||
label: 'Polling Intervall (ms)',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Intervall für Disk-Erkennung.',
|
|
||||||
defaultValue: '4000',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 1000, max: 60000 },
|
|
||||||
orderIndex: 40
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'raw_dir',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Raw Ausgabeordner',
|
|
||||||
type: 'path',
|
|
||||||
required: 1,
|
|
||||||
description: 'Zwischenablage für MakeMKV Rip.',
|
|
||||||
defaultValue: '/mnt/arm-storage/media/raw',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'raw_dir_bluray',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Raw Ausgabeordner (Blu-ray)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler RAW-Zielpfad nur für Blu-ray. Leer = Fallback auf "Raw Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 101
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'raw_dir_dvd',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Raw Ausgabeordner (DVD)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler RAW-Zielpfad nur für DVD. Leer = Fallback auf "Raw Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 102
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'raw_dir_other',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Raw Ausgabeordner (Sonstiges)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler RAW-Zielpfad nur für Sonstiges. Leer = Fallback auf "Raw Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 103
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'movie_dir',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Film Ausgabeordner',
|
|
||||||
type: 'path',
|
|
||||||
required: 1,
|
|
||||||
description: 'Finale HandBrake Ausgabe.',
|
|
||||||
defaultValue: '/mnt/arm-storage/media/movies',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 110
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'movie_dir_bluray',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Film Ausgabeordner (Blu-ray)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler Encode-Zielpfad nur für Blu-ray. Leer = Fallback auf "Film Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 111
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'movie_dir_dvd',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Film Ausgabeordner (DVD)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler Encode-Zielpfad nur für DVD. Leer = Fallback auf "Film Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 112
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'movie_dir_other',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Film Ausgabeordner (Sonstiges)',
|
|
||||||
type: 'path',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler Encode-Zielpfad nur für Sonstiges. Leer = Fallback auf "Film Ausgabeordner".',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 113
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'log_dir',
|
|
||||||
category: 'Pfade',
|
|
||||||
label: 'Log Ordner',
|
|
||||||
type: 'path',
|
|
||||||
required: 1,
|
|
||||||
description: 'Basisordner für Logs. Job-Logs liegen direkt hier, Backend-Logs in /backend.',
|
|
||||||
defaultValue: '/mnt/arm-storage/logs',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 120
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'hardware_monitoring_enabled',
|
|
||||||
category: 'Monitoring',
|
|
||||||
label: 'Hardware Monitoring aktiviert',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Master-Schalter: aktiviert/deaktiviert das komplette Hardware-Monitoring (Polling + Berechnung + WebSocket-Updates).',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 130
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'hardware_monitoring_interval_ms',
|
|
||||||
category: 'Monitoring',
|
|
||||||
label: 'Hardware Monitoring Intervall (ms)',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Polling-Intervall für CPU/RAM/GPU/Storage-Metriken.',
|
|
||||||
defaultValue: '5000',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 1000, max: 60000 },
|
|
||||||
orderIndex: 140
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_command',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Kommando',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Pfad oder Befehl für makemkvcon.',
|
|
||||||
defaultValue: 'makemkvcon',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_registration_key',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Key',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionaler Registrierungsschlüssel. Wird vor Analyze/Rip automatisch per "makemkvcon reg" gesetzt.',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 202
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'mediainfo_command',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Mediainfo Kommando',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Pfad oder Befehl für mediainfo.',
|
|
||||||
defaultValue: 'mediainfo',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 205
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_min_length_minutes',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Minimale Titellänge (Minuten)',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Filtert kurze Titel beim Rip.',
|
|
||||||
defaultValue: '60',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 1, max: 1000 },
|
|
||||||
orderIndex: 210
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_command',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'HandBrake Kommando',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Pfad oder Befehl für HandBrakeCLI.',
|
|
||||||
defaultValue: 'HandBrakeCLI',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 215
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_restart_delete_incomplete_output',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Encode-Neustart: unvollständige Ausgabe löschen',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Wenn aktiv, wird bei "Encode neu starten" der bisherige (nicht erfolgreiche) Output vor Start entfernt.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 220
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pipeline_max_parallel_jobs',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Parallele Jobs',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Maximale Anzahl parallel laufender Jobs. Weitere Starts landen in der Queue.',
|
|
||||||
defaultValue: '1',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 1, max: 12 },
|
|
||||||
orderIndex: 225
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'mediainfo_extra_args_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Mediainfo Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für mediainfo (Blu-ray).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 300
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_rip_mode_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Rip Modus',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'mkv: direkte MKV-Dateien; backup: vollständige Blu-ray Struktur im RAW-Ordner.',
|
|
||||||
defaultValue: 'backup',
|
|
||||||
options: [
|
|
||||||
{ label: 'MKV', value: 'mkv' },
|
|
||||||
{ label: 'Backup', value: 'backup' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 305
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_analyze_extra_args_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Analyze Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für Analyze (Blu-ray).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 310
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_rip_extra_args_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Rip Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für Rip (Blu-ray).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 315
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_preset_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'HandBrake Preset',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Preset Name für -Z (Blu-ray).',
|
|
||||||
defaultValue: 'H.264 MKV 1080p30',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 320
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_extra_args_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'HandBrake Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Argumente (Blu-ray).',
|
|
||||||
defaultValue: '--audio-lang-list deu,eng --first-audio --subtitle-lang-list deu,eng --first-subtitle --aencoder copy --audio-copy-mask ac3,eac3,dts --audio-fallback ac3 --encoder-preset slow --quality 18 --encoder-tune film --encoder-profile high --encoder-level 4.1',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 325
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'output_extension_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Ausgabeformat',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'Dateiendung für finale Datei (Blu-ray).',
|
|
||||||
defaultValue: 'mkv',
|
|
||||||
options: [
|
|
||||||
{ label: 'MKV', value: 'mkv' },
|
|
||||||
{ label: 'MP4', value: 'mp4' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 330
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'filename_template_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Dateiname Template',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Verfügbare Tokens: ${title}, ${year}, ${imdbId} (Blu-ray).',
|
|
||||||
defaultValue: '${title} (${year})',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 335
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'output_folder_template_bluray',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Ordnername Template',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optional. Verfügbare Tokens: ${title}, ${year}, ${imdbId}. Leer = Dateiname-Template (Blu-ray).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 340
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'mediainfo_extra_args_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Mediainfo Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für mediainfo (DVD).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_rip_mode_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Rip Modus',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'mkv: direkte MKV-Dateien; backup: vollständige Disc-Struktur im RAW-Ordner.',
|
|
||||||
defaultValue: 'mkv',
|
|
||||||
options: [
|
|
||||||
{ label: 'MKV', value: 'mkv' },
|
|
||||||
{ label: 'Backup', value: 'backup' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 505
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_analyze_extra_args_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Analyze Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für Analyze (DVD).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 510
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'makemkv_rip_extra_args_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'MakeMKV Rip Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Parameter für Rip (DVD).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 515
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_preset_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'HandBrake Preset',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Preset Name für -Z (DVD).',
|
|
||||||
defaultValue: 'H.264 MKV 480p30',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 520
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'handbrake_extra_args_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'HandBrake Extra Args',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Zusätzliche CLI-Argumente (DVD).',
|
|
||||||
defaultValue: '--audio-lang-list deu,eng --first-audio --subtitle-lang-list deu,eng --first-subtitle --aencoder copy --audio-copy-mask ac3,eac3,dts --audio-fallback ac3 --encoder-preset slow --quality 18 --encoder-tune film --encoder-profile high --encoder-level 4.1',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 525
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'output_extension_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Ausgabeformat',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'Dateiendung für finale Datei (DVD).',
|
|
||||||
defaultValue: 'mkv',
|
|
||||||
options: [
|
|
||||||
{ label: 'MKV', value: 'mkv' },
|
|
||||||
{ label: 'MP4', value: 'mp4' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 530
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'filename_template_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Dateiname Template',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Verfügbare Tokens: ${title}, ${year}, ${imdbId} (DVD).',
|
|
||||||
defaultValue: '${title} (${year})',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 535
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'output_folder_template_dvd',
|
|
||||||
category: 'Tools',
|
|
||||||
label: 'Ordnername Template',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optional. Verfügbare Tokens: ${title}, ${year}, ${imdbId}. Leer = Dateiname-Template (DVD).',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 540
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'omdb_api_key',
|
|
||||||
category: 'Metadaten',
|
|
||||||
label: 'OMDb API Key',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'API Key für Metadatensuche.',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 400
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'omdb_default_type',
|
|
||||||
category: 'Metadaten',
|
|
||||||
label: 'OMDb Typ',
|
|
||||||
type: 'select',
|
|
||||||
required: 1,
|
|
||||||
description: 'Vorauswahl für Suche.',
|
|
||||||
defaultValue: 'movie',
|
|
||||||
options: [
|
|
||||||
{ label: 'Movie', value: 'movie' },
|
|
||||||
{ label: 'Series', value: 'series' },
|
|
||||||
{ label: 'Episode', value: 'episode' }
|
|
||||||
],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 410
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_enabled',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver aktiviert',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Master-Schalter für PushOver Versand.',
|
|
||||||
defaultValue: 'false',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 500
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_token',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver Token',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Application Token für PushOver.',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 510
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_user',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver User',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'User-Key für PushOver.',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 520
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_device',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver Device (optional)',
|
|
||||||
type: 'string',
|
|
||||||
required: 0,
|
|
||||||
description: 'Optionales Ziel-Device in PushOver.',
|
|
||||||
defaultValue: '',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 530
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_title_prefix',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver Titel-Präfix',
|
|
||||||
type: 'string',
|
|
||||||
required: 1,
|
|
||||||
description: 'Prefix im PushOver Titel.',
|
|
||||||
defaultValue: 'Ripster',
|
|
||||||
options: [],
|
|
||||||
validation: { minLength: 1 },
|
|
||||||
orderIndex: 540
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_priority',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver Priority',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'Priorität -2 bis 2.',
|
|
||||||
defaultValue: '0',
|
|
||||||
options: [],
|
|
||||||
validation: { min: -2, max: 2 },
|
|
||||||
orderIndex: 550
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_timeout_ms',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'PushOver Timeout (ms)',
|
|
||||||
type: 'number',
|
|
||||||
required: 1,
|
|
||||||
description: 'HTTP Timeout für PushOver Requests.',
|
|
||||||
defaultValue: '7000',
|
|
||||||
options: [],
|
|
||||||
validation: { min: 1000, max: 60000 },
|
|
||||||
orderIndex: 560
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_metadata_ready',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Metadaten-Auswahl senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet wenn Metadaten zur Auswahl bereitstehen.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 570
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_rip_started',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Rip-Start senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet beim Start des MakeMKV-Rips.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 580
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_encoding_started',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Encode-Start senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet beim Start von HandBrake.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 590
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_job_finished',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Erfolg senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet bei erfolgreich abgeschlossenem Job.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 600
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_job_error',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Fehler senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet bei Fehlern in der Pipeline.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 610
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_job_cancelled',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Abbruch senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet wenn Job manuell abgebrochen wurde.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 620
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_reencode_started',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Re-Encode Start senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet beim Start von RAW Re-Encode.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 630
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'pushover_notify_reencode_finished',
|
|
||||||
category: 'Benachrichtigungen',
|
|
||||||
label: 'Bei Re-Encode Erfolg senden',
|
|
||||||
type: 'boolean',
|
|
||||||
required: 1,
|
|
||||||
description: 'Sendet bei erfolgreichem RAW Re-Encode.',
|
|
||||||
defaultValue: 'true',
|
|
||||||
options: [],
|
|
||||||
validation: {},
|
|
||||||
orderIndex: 640
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
defaultSchema
|
|
||||||
};
|
|
||||||
@@ -376,6 +376,22 @@ function ensureUniqueOutputPath(outputPath) {
|
|||||||
return attempt;
|
return attempt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chownRecursive(targetPath, ownerSpec) {
|
||||||
|
const spec = String(ownerSpec || '').trim();
|
||||||
|
if (!spec || !targetPath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
const result = spawnSync('chown', ['-R', spec, targetPath], { timeout: 15000 });
|
||||||
|
if (result.status !== 0) {
|
||||||
|
logger.warn('chown:failed', { targetPath, spec, stderr: String(result.stderr || '') });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('chown:error', { targetPath, spec, error: error?.message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function moveFileWithFallback(sourcePath, targetPath) {
|
function moveFileWithFallback(sourcePath, targetPath) {
|
||||||
try {
|
try {
|
||||||
fs.renameSync(sourcePath, targetPath);
|
fs.renameSync(sourcePath, targetPath);
|
||||||
@@ -6670,6 +6686,7 @@ class PipelineService extends EventEmitter {
|
|||||||
preferredFinalOutputPath
|
preferredFinalOutputPath
|
||||||
);
|
);
|
||||||
const finalizedOutputPath = outputFinalization.outputPath;
|
const finalizedOutputPath = outputFinalization.outputPath;
|
||||||
|
chownRecursive(path.dirname(finalizedOutputPath), settings.movie_dir_owner);
|
||||||
if (outputFinalization.outputPathWithTimestamp) {
|
if (outputFinalization.outputPathWithTimestamp) {
|
||||||
await historyService.appendLog(
|
await historyService.appendLog(
|
||||||
jobId,
|
jobId,
|
||||||
@@ -6903,6 +6920,7 @@ class PipelineService extends EventEmitter {
|
|||||||
const rawDirName = buildRawDirName(metadataBase, jobId, { incomplete: true });
|
const rawDirName = buildRawDirName(metadataBase, jobId, { incomplete: true });
|
||||||
const rawJobDir = path.join(rawBaseDir, rawDirName);
|
const rawJobDir = path.join(rawBaseDir, rawDirName);
|
||||||
ensureDir(rawJobDir);
|
ensureDir(rawJobDir);
|
||||||
|
chownRecursive(rawJobDir, settings.raw_dir_owner);
|
||||||
logger.info('rip:raw-dir-created', { jobId, rawJobDir });
|
logger.info('rip:raw-dir-created', { jobId, rawJobDir });
|
||||||
|
|
||||||
const deviceCandidate = this.detectedDisc || this.snapshot.context?.device || {
|
const deviceCandidate = this.detectedDisc || this.snapshot.context?.device || {
|
||||||
@@ -7061,6 +7079,7 @@ class PipelineService extends EventEmitter {
|
|||||||
try {
|
try {
|
||||||
fs.renameSync(rawJobDir, completedRawJobDir);
|
fs.renameSync(rawJobDir, completedRawJobDir);
|
||||||
activeRawJobDir = completedRawJobDir;
|
activeRawJobDir = completedRawJobDir;
|
||||||
|
chownRecursive(activeRawJobDir, settings.raw_dir_owner);
|
||||||
await historyService.updateRawPathByOldPath(rawJobDir, completedRawJobDir);
|
await historyService.updateRawPathByOldPath(rawJobDir, completedRawJobDir);
|
||||||
await historyService.appendLog(
|
await historyService.appendLog(
|
||||||
jobId,
|
jobId,
|
||||||
|
|||||||
@@ -35,11 +35,21 @@ const PROFILED_SETTINGS = {
|
|||||||
dvd: 'raw_dir_dvd',
|
dvd: 'raw_dir_dvd',
|
||||||
other: 'raw_dir_other'
|
other: 'raw_dir_other'
|
||||||
},
|
},
|
||||||
|
raw_dir_owner: {
|
||||||
|
bluray: 'raw_dir_bluray_owner',
|
||||||
|
dvd: 'raw_dir_dvd_owner',
|
||||||
|
other: 'raw_dir_other_owner'
|
||||||
|
},
|
||||||
movie_dir: {
|
movie_dir: {
|
||||||
bluray: 'movie_dir_bluray',
|
bluray: 'movie_dir_bluray',
|
||||||
dvd: 'movie_dir_dvd',
|
dvd: 'movie_dir_dvd',
|
||||||
other: 'movie_dir_other'
|
other: 'movie_dir_other'
|
||||||
},
|
},
|
||||||
|
movie_dir_owner: {
|
||||||
|
bluray: 'movie_dir_bluray_owner',
|
||||||
|
dvd: 'movie_dir_dvd_owner',
|
||||||
|
other: 'movie_dir_other_owner'
|
||||||
|
},
|
||||||
mediainfo_extra_args: {
|
mediainfo_extra_args: {
|
||||||
bluray: 'mediainfo_extra_args_bluray',
|
bluray: 'mediainfo_extra_args_bluray',
|
||||||
dvd: 'mediainfo_extra_args_dvd'
|
dvd: 'mediainfo_extra_args_dvd'
|
||||||
@@ -79,7 +89,9 @@ const PROFILED_SETTINGS = {
|
|||||||
};
|
};
|
||||||
const STRICT_PROFILE_ONLY_SETTING_KEYS = new Set([
|
const STRICT_PROFILE_ONLY_SETTING_KEYS = new Set([
|
||||||
'raw_dir',
|
'raw_dir',
|
||||||
'movie_dir'
|
'raw_dir_owner',
|
||||||
|
'movie_dir',
|
||||||
|
'movie_dir_owner'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function applyRuntimeLogDirSetting(rawValue) {
|
function applyRuntimeLogDirSetting(rawValue) {
|
||||||
|
|||||||
265
db/schema.sql
265
db/schema.sql
@@ -131,3 +131,268 @@ CREATE TABLE cron_run_logs (
|
|||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX idx_cron_run_logs_job ON cron_run_logs(cron_job_id, id DESC);
|
CREATE INDEX idx_cron_run_logs_job ON cron_run_logs(cron_job_id, id DESC);
|
||||||
|
|
||||||
|
-- =============================================================================
|
||||||
|
-- Default Settings Seed
|
||||||
|
-- =============================================================================
|
||||||
|
|
||||||
|
-- Pfade – Eigentümer für alternative Verzeichnisse (inline in DynamicSettingsForm gerendert)
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_bluray_owner', 'Pfade', 'Eigentümer Raw-Ordner (Blu-ray)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1015);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_bluray_owner', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_dvd_owner', 'Pfade', 'Eigentümer Raw-Ordner (DVD)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1025);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_dvd_owner', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_other_owner', 'Pfade', 'Eigentümer Raw-Ordner (Sonstiges)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1035);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_other_owner', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_bluray_owner', 'Pfade', 'Eigentümer Film-Ordner (Blu-ray)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1115);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_bluray_owner', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_dvd_owner', 'Pfade', 'Eigentümer Film-Ordner (DVD)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1125);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_dvd_owner', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_other_owner', 'Pfade', 'Eigentümer Film-Ordner (Sonstiges)', 'string', 0, 'Eigentümer der Dateien im Format user:gruppe. Nur aktiv wenn ein alternativer Pfad gesetzt ist. Leer = Standardbenutzer des Dienstes.', NULL, '[]', '{}', 1135);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_other_owner', NULL);
|
||||||
|
|
||||||
|
-- Laufwerk
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('drive_mode', 'Laufwerk', 'Laufwerksmodus', 'select', 1, 'Auto-Discovery oder explizites Device.', 'auto', '[{"label":"Auto Discovery","value":"auto"},{"label":"Explizites Device","value":"explicit"}]', '{}', 10);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('drive_mode', 'auto');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('drive_device', 'Laufwerk', 'Device Pfad', 'path', 0, 'Nur für expliziten Modus, z.B. /dev/sr0.', '/dev/sr0', '[]', '{}', 20);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('drive_device', '/dev/sr0');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_source_index', 'Laufwerk', 'MakeMKV Source Index', 'number', 1, 'Disc Index im Auto-Modus.', '0', '[]', '{"min":0,"max":20}', 30);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_source_index', '0');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('disc_poll_interval_ms', 'Laufwerk', 'Polling Intervall (ms)', 'number', 1, 'Intervall für Disk-Erkennung.', '4000', '[]', '{"min":1000,"max":60000}', 40);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('disc_poll_interval_ms', '4000');
|
||||||
|
|
||||||
|
-- Pfade
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir', 'Pfade', 'Raw Ausgabeordner', 'path', 1, 'Zwischenablage für MakeMKV Rip.', 'data/output/raw', '[]', '{"minLength":1}', 100);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir', 'data/output/raw');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_bluray', 'Pfade', 'Raw Ausgabeordner (Blu-ray)', 'path', 0, 'Optionaler RAW-Zielpfad nur für Blu-ray. Leer = Fallback auf "Raw Ausgabeordner".', NULL, '[]', '{}', 101);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_dvd', 'Pfade', 'Raw Ausgabeordner (DVD)', 'path', 0, 'Optionaler RAW-Zielpfad nur für DVD. Leer = Fallback auf "Raw Ausgabeordner".', NULL, '[]', '{}', 102);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('raw_dir_other', 'Pfade', 'Raw Ausgabeordner (Sonstiges)', 'path', 0, 'Optionaler RAW-Zielpfad nur für Sonstiges. Leer = Fallback auf "Raw Ausgabeordner".', NULL, '[]', '{}', 103);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('raw_dir_other', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir', 'Pfade', 'Film Ausgabeordner', 'path', 1, 'Finale HandBrake Ausgabe.', 'data/output/movies', '[]', '{"minLength":1}', 110);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir', 'data/output/movies');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_bluray', 'Pfade', 'Film Ausgabeordner (Blu-ray)', 'path', 0, 'Optionaler Encode-Zielpfad nur für Blu-ray. Leer = Fallback auf "Film Ausgabeordner".', NULL, '[]', '{}', 111);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_dvd', 'Pfade', 'Film Ausgabeordner (DVD)', 'path', 0, 'Optionaler Encode-Zielpfad nur für DVD. Leer = Fallback auf "Film Ausgabeordner".', NULL, '[]', '{}', 112);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('movie_dir_other', 'Pfade', 'Film Ausgabeordner (Sonstiges)', 'path', 0, 'Optionaler Encode-Zielpfad nur für Sonstiges. Leer = Fallback auf "Film Ausgabeordner".', NULL, '[]', '{}', 113);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('movie_dir_other', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('log_dir', 'Pfade', 'Log Ordner', 'path', 1, 'Basisordner für Logs. Job-Logs liegen direkt hier, Backend-Logs in /backend.', 'data/logs', '[]', '{"minLength":1}', 120);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('log_dir', 'data/logs');
|
||||||
|
|
||||||
|
-- Monitoring
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('hardware_monitoring_enabled', 'Monitoring', 'Hardware Monitoring aktiviert', 'boolean', 1, 'Master-Schalter: aktiviert/deaktiviert das komplette Hardware-Monitoring (Polling + Berechnung + WebSocket-Updates).', 'true', '[]', '{}', 130);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('hardware_monitoring_enabled', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
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');
|
||||||
|
|
||||||
|
-- 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);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_command', 'makemkvcon');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_registration_key', 'Tools', 'MakeMKV Key', 'string', 0, 'Optionaler Registrierungsschlüssel. Wird vor Analyze/Rip automatisch per "makemkvcon reg" gesetzt.', NULL, '[]', '{}', 202);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_registration_key', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('mediainfo_command', 'Tools', 'Mediainfo Kommando', 'string', 1, 'Pfad oder Befehl für mediainfo.', 'mediainfo', '[]', '{"minLength":1}', 205);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('mediainfo_command', 'mediainfo');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_min_length_minutes', 'Tools', 'Minimale Titellänge (Minuten)', 'number', 1, 'Filtert kurze Titel beim Rip.', '60', '[]', '{"min":1,"max":1000}', 210);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_min_length_minutes', '60');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_command', 'Tools', 'HandBrake Kommando', 'string', 1, 'Pfad oder Befehl für HandBrakeCLI.', 'HandBrakeCLI', '[]', '{"minLength":1}', 215);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_command', 'HandBrakeCLI');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_restart_delete_incomplete_output', 'Tools', 'Encode-Neustart: unvollständige Ausgabe löschen', 'boolean', 1, 'Wenn aktiv, wird bei "Encode neu starten" der bisherige (nicht erfolgreiche) Output vor Start entfernt.', 'true', '[]', '{}', 220);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_restart_delete_incomplete_output', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pipeline_max_parallel_jobs', 'Tools', 'Parallele Jobs', 'number', 1, 'Maximale Anzahl parallel laufender Jobs. Weitere Starts landen in der Queue.', '1', '[]', '{"min":1,"max":12}', 225);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pipeline_max_parallel_jobs', '1');
|
||||||
|
|
||||||
|
-- Tools – Blu-ray
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('mediainfo_extra_args_bluray', 'Tools', 'Mediainfo Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für mediainfo (Blu-ray).', NULL, '[]', '{}', 300);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('mediainfo_extra_args_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_rip_mode_bluray', 'Tools', 'MakeMKV Rip Modus', 'select', 1, 'mkv: direkte MKV-Dateien; backup: vollständige Blu-ray Struktur im RAW-Ordner.', 'backup', '[{"label":"MKV","value":"mkv"},{"label":"Backup","value":"backup"}]', '{}', 305);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_rip_mode_bluray', 'backup');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_analyze_extra_args_bluray', 'Tools', 'MakeMKV Analyze Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für Analyze (Blu-ray).', NULL, '[]', '{}', 310);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_analyze_extra_args_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_rip_extra_args_bluray', 'Tools', 'MakeMKV Rip Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für Rip (Blu-ray).', NULL, '[]', '{}', 315);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_rip_extra_args_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_preset_bluray', 'Tools', 'HandBrake Preset', 'string', 1, 'Preset Name für -Z (Blu-ray).', 'H.264 MKV 1080p30', '[]', '{"minLength":1}', 320);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_preset_bluray', 'H.264 MKV 1080p30');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_extra_args_bluray', 'Tools', 'HandBrake Extra Args', 'string', 0, 'Zusätzliche CLI-Argumente (Blu-ray).', NULL, '[]', '{}', 325);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_extra_args_bluray', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('output_extension_bluray', 'Tools', 'Ausgabeformat', 'select', 1, 'Dateiendung für finale Datei (Blu-ray).', 'mkv', '[{"label":"MKV","value":"mkv"},{"label":"MP4","value":"mp4"}]', '{}', 330);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('output_extension_bluray', 'mkv');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('filename_template_bluray', 'Tools', 'Dateiname Template', 'string', 1, 'Verfügbare Tokens: ${title}, ${year}, ${imdbId} (Blu-ray).', '${title} (${year})', '[]', '{"minLength":1}', 335);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('filename_template_bluray', '${title} (${year})');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('output_folder_template_bluray', 'Tools', 'Ordnername Template', 'string', 0, 'Optional. Verfügbare Tokens: ${title}, ${year}, ${imdbId}. Leer = Dateiname-Template (Blu-ray).', NULL, '[]', '{}', 340);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('output_folder_template_bluray', NULL);
|
||||||
|
|
||||||
|
-- Tools – DVD
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('mediainfo_extra_args_dvd', 'Tools', 'Mediainfo Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für mediainfo (DVD).', NULL, '[]', '{}', 500);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('mediainfo_extra_args_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_rip_mode_dvd', 'Tools', 'MakeMKV Rip Modus', 'select', 1, 'mkv: direkte MKV-Dateien; backup: vollständige Disc-Struktur im RAW-Ordner.', 'mkv', '[{"label":"MKV","value":"mkv"},{"label":"Backup","value":"backup"}]', '{}', 505);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_rip_mode_dvd', 'mkv');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_analyze_extra_args_dvd', 'Tools', 'MakeMKV Analyze Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für Analyze (DVD).', NULL, '[]', '{}', 510);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_analyze_extra_args_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('makemkv_rip_extra_args_dvd', 'Tools', 'MakeMKV Rip Extra Args', 'string', 0, 'Zusätzliche CLI-Parameter für Rip (DVD).', NULL, '[]', '{}', 515);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('makemkv_rip_extra_args_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_preset_dvd', 'Tools', 'HandBrake Preset', 'string', 1, 'Preset Name für -Z (DVD).', 'H.264 MKV 480p30', '[]', '{"minLength":1}', 520);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_preset_dvd', 'H.264 MKV 480p30');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('handbrake_extra_args_dvd', 'Tools', 'HandBrake Extra Args', 'string', 0, 'Zusätzliche CLI-Argumente (DVD).', NULL, '[]', '{}', 525);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('handbrake_extra_args_dvd', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('output_extension_dvd', 'Tools', 'Ausgabeformat', 'select', 1, 'Dateiendung für finale Datei (DVD).', 'mkv', '[{"label":"MKV","value":"mkv"},{"label":"MP4","value":"mp4"}]', '{}', 530);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('output_extension_dvd', 'mkv');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('filename_template_dvd', 'Tools', 'Dateiname Template', 'string', 1, 'Verfügbare Tokens: ${title}, ${year}, ${imdbId} (DVD).', '${title} (${year})', '[]', '{"minLength":1}', 535);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('filename_template_dvd', '${title} (${year})');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('output_folder_template_dvd', 'Tools', 'Ordnername Template', 'string', 0, 'Optional. Verfügbare Tokens: ${title}, ${year}, ${imdbId}. Leer = Dateiname-Template (DVD).', NULL, '[]', '{}', 540);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('output_folder_template_dvd', NULL);
|
||||||
|
|
||||||
|
-- Metadaten
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('omdb_api_key', 'Metadaten', 'OMDb API Key', 'string', 0, 'API Key für Metadatensuche.', NULL, '[]', '{}', 400);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('omdb_api_key', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('omdb_default_type', 'Metadaten', 'OMDb Typ', 'select', 1, 'Vorauswahl für Suche.', 'movie', '[{"label":"Movie","value":"movie"},{"label":"Series","value":"series"},{"label":"Episode","value":"episode"}]', '{}', 410);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('omdb_default_type', 'movie');
|
||||||
|
|
||||||
|
-- Benachrichtigungen
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_enabled', 'Benachrichtigungen', 'PushOver aktiviert', 'boolean', 1, 'Master-Schalter für PushOver Versand.', 'false', '[]', '{}', 500);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_enabled', 'false');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_token', 'Benachrichtigungen', 'PushOver Token', 'string', 0, 'Application Token für PushOver.', NULL, '[]', '{}', 510);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_token', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_user', 'Benachrichtigungen', 'PushOver User', 'string', 0, 'User-Key für PushOver.', NULL, '[]', '{}', 520);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_user', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_device', 'Benachrichtigungen', 'PushOver Device (optional)', 'string', 0, 'Optionales Ziel-Device in PushOver.', NULL, '[]', '{}', 530);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_device', NULL);
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_title_prefix', 'Benachrichtigungen', 'PushOver Titel-Präfix', 'string', 1, 'Prefix im PushOver Titel.', 'Ripster', '[]', '{"minLength":1}', 540);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_title_prefix', 'Ripster');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_priority', 'Benachrichtigungen', 'PushOver Priority', 'number', 1, 'Priorität -2 bis 2.', '0', '[]', '{"min":-2,"max":2}', 550);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_priority', '0');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_timeout_ms', 'Benachrichtigungen', 'PushOver Timeout (ms)', 'number', 1, 'HTTP Timeout für PushOver Requests.', '7000', '[]', '{"min":1000,"max":60000}', 560);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_timeout_ms', '7000');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_metadata_ready', 'Benachrichtigungen', 'Bei Metadaten-Auswahl senden', 'boolean', 1, 'Sendet wenn Metadaten zur Auswahl bereitstehen.', 'true', '[]', '{}', 570);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_metadata_ready', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_rip_started', 'Benachrichtigungen', 'Bei Rip-Start senden', 'boolean', 1, 'Sendet beim Start des MakeMKV-Rips.', 'true', '[]', '{}', 580);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_rip_started', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_encoding_started', 'Benachrichtigungen', 'Bei Encode-Start senden', 'boolean', 1, 'Sendet beim Start von HandBrake.', 'true', '[]', '{}', 590);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_encoding_started', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_job_finished', 'Benachrichtigungen', 'Bei Erfolg senden', 'boolean', 1, 'Sendet bei erfolgreich abgeschlossenem Job.', 'true', '[]', '{}', 600);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_job_finished', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_job_error', 'Benachrichtigungen', 'Bei Fehler senden', 'boolean', 1, 'Sendet bei Fehlern in der Pipeline.', 'true', '[]', '{}', 610);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_job_error', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_job_cancelled', 'Benachrichtigungen', 'Bei Abbruch senden', 'boolean', 1, 'Sendet wenn Job manuell abgebrochen wurde.', 'true', '[]', '{}', 620);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_job_cancelled', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_reencode_started', 'Benachrichtigungen', 'Bei Re-Encode Start senden', 'boolean', 1, 'Sendet beim Start von RAW Re-Encode.', 'true', '[]', '{}', 630);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_reencode_started', 'true');
|
||||||
|
|
||||||
|
INSERT OR IGNORE INTO settings_schema (key, category, label, type, required, description, default_value, options_json, validation_json, order_index)
|
||||||
|
VALUES ('pushover_notify_reencode_finished', 'Benachrichtigungen', 'Bei Re-Encode Erfolg senden', 'boolean', 1, 'Sendet bei erfolgreichem RAW Re-Encode.', 'true', '[]', '{}', 640);
|
||||||
|
INSERT OR IGNORE INTO settings_values (key, value) VALUES ('pushover_notify_reencode_finished', 'true');
|
||||||
|
|||||||
@@ -161,12 +161,30 @@ export default function DynamicSettingsForm({
|
|||||||
{section.description ? <small>{section.description}</small> : null}
|
{section.description ? <small>{section.description}</small> : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
{(() => {
|
||||||
|
const ownerKeySet = new Set(
|
||||||
|
(section.settings || [])
|
||||||
|
.filter((s) => String(s.key || '').endsWith('_owner'))
|
||||||
|
.map((s) => s.key)
|
||||||
|
);
|
||||||
|
const settingsByKey = new Map(
|
||||||
|
(section.settings || []).map((s) => [s.key, s])
|
||||||
|
);
|
||||||
|
const visibleSettings = (section.settings || []).filter(
|
||||||
|
(s) => !ownerKeySet.has(s.key)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="settings-grid">
|
<div className="settings-grid">
|
||||||
{(section.settings || []).map((setting) => {
|
{visibleSettings.map((setting) => {
|
||||||
const value = values?.[setting.key];
|
const value = values?.[setting.key];
|
||||||
const error = errors?.[setting.key] || null;
|
const error = errors?.[setting.key] || null;
|
||||||
const dirty = Boolean(dirtyKeys?.has?.(setting.key));
|
const dirty = Boolean(dirtyKeys?.has?.(setting.key));
|
||||||
|
|
||||||
|
const ownerKey = `${setting.key}_owner`;
|
||||||
|
const ownerSetting = settingsByKey.get(ownerKey) || null;
|
||||||
|
const pathHasValue = Boolean(String(value ?? '').trim());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={setting.key} className="setting-row">
|
<div key={setting.key} className="setting-row">
|
||||||
<label htmlFor={setting.key}>
|
<label htmlFor={setting.key}>
|
||||||
@@ -234,10 +252,36 @@ export default function DynamicSettingsForm({
|
|||||||
className="saved-tag"
|
className="saved-tag"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{ownerSetting ? (
|
||||||
|
<div className="setting-owner-row">
|
||||||
|
<label htmlFor={ownerKey} className="setting-owner-label">
|
||||||
|
Eigentümer (user:gruppe)
|
||||||
|
</label>
|
||||||
|
<InputText
|
||||||
|
id={ownerKey}
|
||||||
|
value={values?.[ownerKey] ?? ''}
|
||||||
|
placeholder="z.B. michael:ripster"
|
||||||
|
disabled={!pathHasValue}
|
||||||
|
onChange={(event) => onChange?.(ownerKey, event.target.value)}
|
||||||
|
/>
|
||||||
|
{errors?.[ownerKey] ? (
|
||||||
|
<small className="error-text">{errors[ownerKey]}</small>
|
||||||
|
) : (
|
||||||
|
<Tag
|
||||||
|
value={dirtyKeys?.has?.(ownerKey) ? 'Ungespeichert' : 'Gespeichert'}
|
||||||
|
severity={dirtyKeys?.has?.(ownerKey) ? 'warning' : 'success'}
|
||||||
|
className="saved-tag"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</section>
|
</section>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1100,6 +1100,29 @@ body {
|
|||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.setting-owner-row {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
padding-top: 0.5rem;
|
||||||
|
border-top: 1px dashed var(--rip-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-owner-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--rip-muted);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-owner-row .p-inputtext:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-owner-row .saved-tag {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.script-manager-wrap {
|
.script-manager-wrap {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.8rem;
|
gap: 0.8rem;
|
||||||
|
|||||||
18
install.sh
18
install.sh
@@ -413,6 +413,9 @@ fi
|
|||||||
# Daten- und Log-Verzeichnisse sicherstellen
|
# Daten- und Log-Verzeichnisse sicherstellen
|
||||||
mkdir -p "$INSTALL_DIR/backend/data"
|
mkdir -p "$INSTALL_DIR/backend/data"
|
||||||
mkdir -p "$INSTALL_DIR/backend/logs"
|
mkdir -p "$INSTALL_DIR/backend/logs"
|
||||||
|
mkdir -p "$INSTALL_DIR/backend/data/output/raw"
|
||||||
|
mkdir -p "$INSTALL_DIR/backend/data/output/movies"
|
||||||
|
mkdir -p "$INSTALL_DIR/backend/data/logs"
|
||||||
|
|
||||||
# Gesicherte Daten zurückspielen
|
# Gesicherte Daten zurückspielen
|
||||||
if [[ -n "${DATA_BACKUP:-}" && -d "$DATA_BACKUP" ]]; then
|
if [[ -n "${DATA_BACKUP:-}" && -d "$DATA_BACKUP" ]]; then
|
||||||
@@ -477,6 +480,21 @@ chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR"
|
|||||||
chmod -R 755 "$INSTALL_DIR"
|
chmod -R 755 "$INSTALL_DIR"
|
||||||
chmod 600 "$ENV_FILE"
|
chmod 600 "$ENV_FILE"
|
||||||
|
|
||||||
|
# Ausgabe- und Log-Verzeichnisse dem installierenden User zuweisen
|
||||||
|
# (SUDO_USER = der echte User hinter sudo; leer wenn direkt als root ausgeführt)
|
||||||
|
ACTUAL_USER="${SUDO_USER:-}"
|
||||||
|
if [[ -n "$ACTUAL_USER" && "$ACTUAL_USER" != "root" ]]; then
|
||||||
|
chown -R "$ACTUAL_USER:$SERVICE_USER" \
|
||||||
|
"$INSTALL_DIR/backend/data/output" \
|
||||||
|
"$INSTALL_DIR/backend/data/logs"
|
||||||
|
chmod -R 775 \
|
||||||
|
"$INSTALL_DIR/backend/data/output" \
|
||||||
|
"$INSTALL_DIR/backend/data/logs"
|
||||||
|
ok "Verzeichnisse $ACTUAL_USER:$SERVICE_USER (775) zugewiesen"
|
||||||
|
else
|
||||||
|
ok "Verzeichnisse bereits $SERVICE_USER gehörig (kein SUDO_USER erkannt)"
|
||||||
|
fi
|
||||||
|
|
||||||
# --- Systemd-Dienst: Backend -------------------------------------------------
|
# --- Systemd-Dienst: Backend -------------------------------------------------
|
||||||
header "Systemd-Dienst (Backend) erstellen"
|
header "Systemd-Dienst (Backend) erstellen"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user