0.16.1-5 Ebcode Fix / presets
This commit is contained in:
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "ripster-backend",
|
||||
"version": "0.16.1-4",
|
||||
"version": "0.16.1-5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ripster-backend",
|
||||
"version": "0.16.1-4",
|
||||
"version": "0.16.1-5",
|
||||
"dependencies": {
|
||||
"archiver": "^7.0.1",
|
||||
"cors": "^2.8.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripster-backend",
|
||||
"version": "0.16.1-4",
|
||||
"version": "0.16.1-5",
|
||||
"private": true,
|
||||
"type": "commonjs",
|
||||
"scripts": {
|
||||
|
||||
@@ -532,6 +532,9 @@ router.post(
|
||||
'/confirm-encode/:jobId',
|
||||
asyncHandler(async (req, res) => {
|
||||
const jobId = Number(req.params.jobId);
|
||||
const body = req.body && typeof req.body === 'object' ? req.body : {};
|
||||
const hasSelectedUserPresetId = Object.prototype.hasOwnProperty.call(body, 'selectedUserPresetId');
|
||||
const hasSelectedHandBrakePreset = Object.prototype.hasOwnProperty.call(body, 'selectedHandBrakePreset');
|
||||
const selectedEncodeTitleId = req.body?.selectedEncodeTitleId ?? null;
|
||||
const selectedEncodeTitleIds = req.body?.selectedEncodeTitleIds ?? null;
|
||||
const selectedTrackSelection = req.body?.selectedTrackSelection ?? null;
|
||||
@@ -541,8 +544,12 @@ router.post(
|
||||
const selectedPostEncodeChainIds = req.body?.selectedPostEncodeChainIds;
|
||||
const selectedPreEncodeChainIds = req.body?.selectedPreEncodeChainIds;
|
||||
const skipPipelineStateUpdate = Boolean(req.body?.skipPipelineStateUpdate);
|
||||
const selectedUserPresetId = req.body?.selectedUserPresetId ?? null;
|
||||
const selectedHandBrakePreset = req.body?.selectedHandBrakePreset ?? null;
|
||||
const selectedUserPresetId = hasSelectedUserPresetId
|
||||
? body.selectedUserPresetId
|
||||
: undefined;
|
||||
const selectedHandBrakePreset = hasSelectedHandBrakePreset
|
||||
? body.selectedHandBrakePreset
|
||||
: undefined;
|
||||
logger.info('post:confirm-encode', {
|
||||
reqId: req.reqId,
|
||||
jobId,
|
||||
|
||||
@@ -8,6 +8,7 @@ const pipelineService = require('../services/pipelineService');
|
||||
const wsService = require('../services/websocketService');
|
||||
const hardwareMonitorService = require('../services/hardwareMonitorService');
|
||||
const userPresetService = require('../services/userPresetService');
|
||||
const userPresetDefaultsService = require('../services/userPresetDefaultsService');
|
||||
const activationBytesService = require('../services/activationBytesService');
|
||||
const diskDetectionService = require('../services/diskDetectionService');
|
||||
const coverArtRecoveryService = require('../services/coverArtRecoveryService');
|
||||
@@ -277,6 +278,72 @@ router.post(
|
||||
})
|
||||
);
|
||||
|
||||
// ── User Presets ──────────────────────────────────────────────────────────────
|
||||
|
||||
router.get(
|
||||
'/user-presets',
|
||||
asyncHandler(async (req, res) => {
|
||||
const mediaType = req.query.media_type || null;
|
||||
logger.debug('get:user-presets', { reqId: req.reqId, mediaType });
|
||||
const presets = await userPresetService.listPresets(mediaType);
|
||||
res.json({ presets });
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/user-preset-defaults',
|
||||
asyncHandler(async (req, res) => {
|
||||
logger.debug('get:user-preset-defaults', { reqId: req.reqId });
|
||||
const defaults = await userPresetDefaultsService.listDefaults();
|
||||
res.json({ defaults });
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/user-preset-defaults',
|
||||
asyncHandler(async (req, res) => {
|
||||
const payload = req.body || {};
|
||||
logger.info('put:user-preset-defaults:update', { reqId: req.reqId });
|
||||
const defaults = await userPresetDefaultsService.updateDefaults(payload);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'defaults-updated' });
|
||||
res.json({ defaults });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/user-presets',
|
||||
asyncHandler(async (req, res) => {
|
||||
const payload = req.body || {};
|
||||
logger.info('post:user-presets:create', { reqId: req.reqId, name: payload?.name });
|
||||
const preset = await userPresetService.createPreset(payload);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'created', id: preset.id });
|
||||
res.status(201).json({ preset });
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/user-presets/:id',
|
||||
asyncHandler(async (req, res) => {
|
||||
const presetId = Number(req.params.id);
|
||||
const payload = req.body || {};
|
||||
logger.info('put:user-presets:update', { reqId: req.reqId, presetId });
|
||||
const preset = await userPresetService.updatePreset(presetId, payload);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'updated', id: preset.id });
|
||||
res.json({ preset });
|
||||
})
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/user-presets/:id',
|
||||
asyncHandler(async (req, res) => {
|
||||
const presetId = Number(req.params.id);
|
||||
logger.info('delete:user-presets', { reqId: req.reqId, presetId });
|
||||
const removed = await userPresetService.deletePreset(presetId);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'deleted', id: removed.id });
|
||||
res.json({ removed });
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/:key',
|
||||
asyncHandler(async (req, res) => {
|
||||
@@ -408,52 +475,6 @@ router.put(
|
||||
})
|
||||
);
|
||||
|
||||
// ── User Presets ──────────────────────────────────────────────────────────────
|
||||
|
||||
router.get(
|
||||
'/user-presets',
|
||||
asyncHandler(async (req, res) => {
|
||||
const mediaType = req.query.media_type || null;
|
||||
logger.debug('get:user-presets', { reqId: req.reqId, mediaType });
|
||||
const presets = await userPresetService.listPresets(mediaType);
|
||||
res.json({ presets });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/user-presets',
|
||||
asyncHandler(async (req, res) => {
|
||||
const payload = req.body || {};
|
||||
logger.info('post:user-presets:create', { reqId: req.reqId, name: payload?.name });
|
||||
const preset = await userPresetService.createPreset(payload);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'created', id: preset.id });
|
||||
res.status(201).json({ preset });
|
||||
})
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/user-presets/:id',
|
||||
asyncHandler(async (req, res) => {
|
||||
const presetId = Number(req.params.id);
|
||||
const payload = req.body || {};
|
||||
logger.info('put:user-presets:update', { reqId: req.reqId, presetId });
|
||||
const preset = await userPresetService.updatePreset(presetId, payload);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'updated', id: preset.id });
|
||||
res.json({ preset });
|
||||
})
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/user-presets/:id',
|
||||
asyncHandler(async (req, res) => {
|
||||
const presetId = Number(req.params.id);
|
||||
logger.info('delete:user-presets', { reqId: req.reqId, presetId });
|
||||
const removed = await userPresetService.deletePreset(presetId);
|
||||
wsService.broadcast('USER_PRESETS_UPDATED', { action: 'deleted', id: removed.id });
|
||||
res.json({ removed });
|
||||
})
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/pushover/test',
|
||||
asyncHandler(async (req, res) => {
|
||||
|
||||
@@ -27,6 +27,7 @@ const { buildMediainfoReview, refreshAudioTrackActionsForPlanTitles } = require(
|
||||
const { analyzePlaylistObfuscation, normalizePlaylistId } = require('../utils/playlistAnalysis');
|
||||
const { errorToMeta } = require('../utils/errorMeta');
|
||||
const userPresetService = require('./userPresetService');
|
||||
const userPresetDefaultsService = require('./userPresetDefaultsService');
|
||||
const thumbnailService = require('./thumbnailService');
|
||||
const activationBytesService = require('./activationBytesService');
|
||||
const { toBoolean } = require('../utils/validators');
|
||||
@@ -8529,6 +8530,119 @@ function normalizeUserPresetForPlan(rawPreset) {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveUserPresetDefaultSlotKey(mediaProfile, isSeriesSelection = false) {
|
||||
const normalizedProfile = normalizeMediaProfile(mediaProfile);
|
||||
if (normalizedProfile !== 'dvd' && normalizedProfile !== 'bluray') {
|
||||
return null;
|
||||
}
|
||||
return `${normalizedProfile}_${isSeriesSelection ? 'series' : 'movie'}`;
|
||||
}
|
||||
|
||||
async function applyConfiguredDefaultUserPresetToReviewPlan(reviewPlan, options = {}) {
|
||||
const plan = reviewPlan && typeof reviewPlan === 'object'
|
||||
? reviewPlan
|
||||
: null;
|
||||
if (!plan) {
|
||||
return {
|
||||
plan: reviewPlan,
|
||||
applied: false,
|
||||
slotKey: null,
|
||||
presetId: null,
|
||||
presetName: null
|
||||
};
|
||||
}
|
||||
|
||||
const existingUserPreset = normalizeUserPresetForPlan(plan?.userPreset || null);
|
||||
if (existingUserPreset) {
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey: null,
|
||||
presetId: existingUserPreset.id || null,
|
||||
presetName: existingUserPreset.name || null
|
||||
};
|
||||
}
|
||||
|
||||
const slotKey = resolveUserPresetDefaultSlotKey(options?.mediaProfile || null, Boolean(options?.isSeriesSelection));
|
||||
if (!slotKey) {
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey: null,
|
||||
presetId: null,
|
||||
presetName: null
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const defaultsMap = await userPresetDefaultsService.listDefaults();
|
||||
const rawPresetId = defaultsMap && typeof defaultsMap === 'object'
|
||||
? defaultsMap[slotKey]
|
||||
: null;
|
||||
const presetId = normalizePositiveInteger(rawPresetId);
|
||||
if (!presetId) {
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey,
|
||||
presetId: null,
|
||||
presetName: null
|
||||
};
|
||||
}
|
||||
|
||||
const preset = await userPresetService.getPresetById(presetId);
|
||||
const normalizedPreset = normalizeUserPresetForPlan(preset);
|
||||
if (!normalizedPreset) {
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey,
|
||||
presetId,
|
||||
presetName: null
|
||||
};
|
||||
}
|
||||
|
||||
const presetMediaType = String(preset?.mediaType || '').trim().toLowerCase();
|
||||
const normalizedProfile = normalizeMediaProfile(options?.mediaProfile || null);
|
||||
const mediaTypeAllowed = presetMediaType === 'all'
|
||||
|| !presetMediaType
|
||||
|| presetMediaType === normalizedProfile;
|
||||
if (!mediaTypeAllowed) {
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey,
|
||||
presetId,
|
||||
presetName: normalizedPreset.name || null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
plan: {
|
||||
...plan,
|
||||
userPreset: normalizedPreset
|
||||
},
|
||||
applied: true,
|
||||
slotKey,
|
||||
presetId: normalizedPreset.id || presetId || null,
|
||||
presetName: normalizedPreset.name || null
|
||||
};
|
||||
} catch (error) {
|
||||
logger.warn('review:user-preset-default:resolve-failed', {
|
||||
mediaProfile: options?.mediaProfile || null,
|
||||
isSeriesSelection: Boolean(options?.isSeriesSelection),
|
||||
error: errorToMeta(error)
|
||||
});
|
||||
return {
|
||||
plan,
|
||||
applied: false,
|
||||
slotKey,
|
||||
presetId: null,
|
||||
presetName: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function buildScriptDescriptorList(scriptIds, sourceScripts = []) {
|
||||
const normalizedIds = normalizeScriptIdList(scriptIds);
|
||||
if (normalizedIds.length === 0) {
|
||||
@@ -18257,6 +18371,11 @@ class PipelineService extends EventEmitter {
|
||||
options?.multipartSettingsLock || null
|
||||
);
|
||||
review = multipartLockResult.plan;
|
||||
const reviewDefaultUserPresetResult = await applyConfiguredDefaultUserPresetToReviewPlan(review, {
|
||||
mediaProfile,
|
||||
isSeriesSelection: true
|
||||
});
|
||||
review = reviewDefaultUserPresetResult.plan;
|
||||
resolvedSelectedTitleIds = normalizeReviewTitleIdList(review.selectedTitleIds || []);
|
||||
resolvedPrimaryTitleId = normalizeReviewTitleId(review.encodeInputTitleId)
|
||||
|| resolvedSelectedTitleIds[0]
|
||||
@@ -18931,6 +19050,11 @@ class PipelineService extends EventEmitter {
|
||||
options?.multipartSettingsLock || null
|
||||
);
|
||||
review = multipartLockResult.plan;
|
||||
const reviewDefaultUserPresetResult = await applyConfiguredDefaultUserPresetToReviewPlan(review, {
|
||||
mediaProfile,
|
||||
isSeriesSelection: isSeriesDvdMetadataSelection(mediaProfile, selectedMetadata, analyzeContext)
|
||||
});
|
||||
review = reviewDefaultUserPresetResult.plan;
|
||||
|
||||
if (!Array.isArray(review.titles) || review.titles.length === 0) {
|
||||
const error = new Error('Titel-/Spurprüfung aus RAW lieferte keine Titel.');
|
||||
@@ -22014,29 +22138,26 @@ class PipelineService extends EventEmitter {
|
||||
}
|
||||
|
||||
// Resolve user preset: explicit payload wins, otherwise preserve currently selected preset from encode plan.
|
||||
const hasExplicitUserPresetSelection = !multipartSettingsLocked
|
||||
&& Object.prototype.hasOwnProperty.call(options || {}, 'selectedUserPresetId');
|
||||
const hasExplicitHandBrakePresetSelection = !multipartSettingsLocked
|
||||
&& Object.prototype.hasOwnProperty.call(options || {}, 'selectedHandBrakePreset');
|
||||
const rawHandBrakePreset = hasExplicitHandBrakePresetSelection
|
||||
const hasUserPresetIdField = Object.prototype.hasOwnProperty.call(options || {}, 'selectedUserPresetId');
|
||||
const hasHandBrakePresetField = Object.prototype.hasOwnProperty.call(options || {}, 'selectedHandBrakePreset');
|
||||
const explicitUserPresetId = hasUserPresetIdField
|
||||
? normalizePositiveInteger(options?.selectedUserPresetId)
|
||||
: null;
|
||||
const rawHandBrakePreset = hasHandBrakePresetField
|
||||
? String(options?.selectedHandBrakePreset || '').trim()
|
||||
: '';
|
||||
const hasExplicitUserPresetSelection = !multipartSettingsLocked && explicitUserPresetId !== null;
|
||||
const hasExplicitHandBrakePresetSelection = !multipartSettingsLocked && Boolean(rawHandBrakePreset);
|
||||
let resolvedUserPreset = null;
|
||||
if (hasExplicitUserPresetSelection) {
|
||||
const rawUserPresetId = options?.selectedUserPresetId;
|
||||
const userPresetId = rawUserPresetId !== null && rawUserPresetId !== undefined && String(rawUserPresetId).trim() !== ''
|
||||
? Number(rawUserPresetId)
|
||||
: null;
|
||||
if (Number.isFinite(userPresetId) && userPresetId > 0) {
|
||||
resolvedUserPreset = await userPresetService.getPresetById(userPresetId);
|
||||
if (!resolvedUserPreset) {
|
||||
const error = new Error(`User-Preset ${userPresetId} nicht gefunden.`);
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
resolvedUserPreset = await userPresetService.getPresetById(explicitUserPresetId);
|
||||
if (!resolvedUserPreset) {
|
||||
const error = new Error(`User-Preset ${explicitUserPresetId} nicht gefunden.`);
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if ((!resolvedUserPreset || !hasExplicitUserPresetSelection) && hasExplicitHandBrakePresetSelection) {
|
||||
if (!resolvedUserPreset && hasExplicitHandBrakePresetSelection) {
|
||||
resolvedUserPreset = rawHandBrakePreset
|
||||
? {
|
||||
name: `HandBrake: ${rawHandBrakePreset}`,
|
||||
@@ -22045,8 +22166,10 @@ class PipelineService extends EventEmitter {
|
||||
}
|
||||
: null;
|
||||
}
|
||||
if (!hasExplicitUserPresetSelection && !hasExplicitHandBrakePresetSelection) {
|
||||
resolvedUserPreset = normalizeUserPresetForPlan(encodePlan?.userPreset || null);
|
||||
if (!resolvedUserPreset) {
|
||||
resolvedUserPreset = normalizeUserPresetForPlan(
|
||||
planForConfirm?.userPreset || encodePlan?.userPreset || null
|
||||
);
|
||||
}
|
||||
|
||||
const confirmedPlan = {
|
||||
@@ -22070,24 +22193,49 @@ class PipelineService extends EventEmitter {
|
||||
const readyMediaProfile = this.resolveMediaProfileForJob(job, {
|
||||
encodePlan: confirmedPlan
|
||||
});
|
||||
if (readyMediaProfile === 'converter' && !confirmedPlan.userPreset) {
|
||||
const error = new Error('Bestätigung nicht möglich: Bitte ein User- oder HandBrake-Preset auswählen.');
|
||||
const confirmSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
||||
const confirmedConverterMediaType = String(
|
||||
confirmedPlan?.converterMediaType || encodePlan?.converterMediaType || ''
|
||||
).trim().toLowerCase();
|
||||
const requiresExplicitPreset = readyMediaProfile === 'dvd'
|
||||
|| readyMediaProfile === 'bluray'
|
||||
|| (readyMediaProfile === 'converter' && confirmedConverterMediaType !== 'audio');
|
||||
const planPresetName = String(
|
||||
planForConfirm?.userPreset?.handbrakePreset
|
||||
|| encodePlan?.userPreset?.handbrakePreset
|
||||
|| planForConfirm?.selectors?.preset
|
||||
|| encodePlan?.selectors?.preset
|
||||
|| ''
|
||||
).trim();
|
||||
const planExtraArgs = String(
|
||||
planForConfirm?.userPreset?.extraArgs
|
||||
|| encodePlan?.userPreset?.extraArgs
|
||||
|| planForConfirm?.selectors?.extraArgs
|
||||
|| encodePlan?.selectors?.extraArgs
|
||||
|| ''
|
||||
).trim();
|
||||
const effectivePresetName = String(
|
||||
resolvedUserPreset?.handbrakePreset
|
||||
|| rawHandBrakePreset
|
||||
|| planPresetName
|
||||
|| confirmSettings?.handbrake_preset
|
||||
|| ''
|
||||
).trim();
|
||||
const effectiveExtraArgs = String(
|
||||
resolvedUserPreset?.extraArgs
|
||||
|| planExtraArgs
|
||||
|| confirmSettings?.handbrake_extra_args
|
||||
|| ''
|
||||
).trim();
|
||||
if (requiresExplicitPreset && !effectivePresetName && !effectiveExtraArgs) {
|
||||
const error = new Error('Bestätigung nicht möglich: Kein Preset oder Extra-Args gesetzt. Bitte User-/HandBrake-Preset wählen oder passende Settings hinterlegen.');
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
const confirmSettings = await settingsService.getEffectiveSettingsMap(readyMediaProfile);
|
||||
const actionDisplaySettings = {
|
||||
...(confirmSettings && typeof confirmSettings === 'object' ? confirmSettings : {}),
|
||||
handbrake_preset: String(
|
||||
resolvedUserPreset?.handbrakePreset
|
||||
|| confirmSettings?.handbrake_preset
|
||||
|| ''
|
||||
).trim() || null,
|
||||
handbrake_extra_args: String(
|
||||
resolvedUserPreset?.extraArgs
|
||||
|| confirmSettings?.handbrake_extra_args
|
||||
|| ''
|
||||
).trim()
|
||||
handbrake_preset: effectivePresetName || null,
|
||||
handbrake_extra_args: effectiveExtraArgs
|
||||
};
|
||||
confirmedPlan.titles = refreshAudioTrackActionsForPlanTitles(
|
||||
confirmedPlan.titles,
|
||||
@@ -23315,6 +23463,11 @@ class PipelineService extends EventEmitter {
|
||||
options?.multipartSettingsLock || null
|
||||
);
|
||||
enrichedReview = multipartLockResult.plan;
|
||||
const reviewDefaultUserPresetResult = await applyConfiguredDefaultUserPresetToReviewPlan(enrichedReview, {
|
||||
mediaProfile,
|
||||
isSeriesSelection: effectiveIsSeriesDvdReview
|
||||
});
|
||||
enrichedReview = reviewDefaultUserPresetResult.plan;
|
||||
const previousEncodePlan = options?.previousEncodePlan && typeof options.previousEncodePlan === 'object'
|
||||
? options.previousEncodePlan
|
||||
: null;
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
const { getDb } = require('../db/database');
|
||||
const logger = require('./logger').child('USER_PRESET_DEFAULTS');
|
||||
|
||||
const SLOT_KEYS = Object.freeze([
|
||||
'bluray_movie',
|
||||
'bluray_series',
|
||||
'dvd_movie',
|
||||
'dvd_series'
|
||||
]);
|
||||
const SLOT_KEY_SET = new Set(SLOT_KEYS);
|
||||
const SLOT_ALLOWED_MEDIA_TYPES = Object.freeze({
|
||||
bluray_movie: new Set(['bluray', 'all']),
|
||||
bluray_series: new Set(['bluray', 'all']),
|
||||
dvd_movie: new Set(['dvd', 'all']),
|
||||
dvd_series: new Set(['dvd', 'all'])
|
||||
});
|
||||
|
||||
function normalizePresetId(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
if (typeof value === 'string' && value.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric) || numeric <= 0) {
|
||||
return null;
|
||||
}
|
||||
return Math.trunc(numeric);
|
||||
}
|
||||
|
||||
function buildDefaultsMap(rows = []) {
|
||||
const map = Object.fromEntries(SLOT_KEYS.map((key) => [key, null]));
|
||||
for (const row of rows) {
|
||||
const slotKey = String(row?.slot_key || '').trim();
|
||||
if (!SLOT_KEY_SET.has(slotKey)) {
|
||||
continue;
|
||||
}
|
||||
const presetId = normalizePresetId(row?.preset_id);
|
||||
map[slotKey] = presetId;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
async function listDefaults() {
|
||||
const db = await getDb();
|
||||
const rows = await db.all(
|
||||
`
|
||||
SELECT slot_key, preset_id
|
||||
FROM user_preset_defaults
|
||||
WHERE slot_key IN (${SLOT_KEYS.map(() => '?').join(', ')})
|
||||
ORDER BY slot_key ASC
|
||||
`,
|
||||
SLOT_KEYS
|
||||
);
|
||||
return buildDefaultsMap(rows);
|
||||
}
|
||||
|
||||
async function updateDefaults(payload = {}) {
|
||||
const defaults = payload && typeof payload === 'object'
|
||||
? (payload.defaults && typeof payload.defaults === 'object' ? payload.defaults : payload)
|
||||
: {};
|
||||
const updates = [];
|
||||
|
||||
for (const [slotKey, rawPresetId] of Object.entries(defaults)) {
|
||||
if (!SLOT_KEY_SET.has(slotKey)) {
|
||||
const error = new Error(`Ungültiger Slot: ${slotKey}`);
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
updates.push({ slotKey, presetId: normalizePresetId(rawPresetId) });
|
||||
}
|
||||
|
||||
if (updates.length === 0) {
|
||||
return listDefaults();
|
||||
}
|
||||
|
||||
const db = await getDb();
|
||||
for (const update of updates) {
|
||||
if (update.presetId === null) {
|
||||
continue;
|
||||
}
|
||||
const exists = await db.get(
|
||||
`SELECT id, media_type FROM user_presets WHERE id = ? LIMIT 1`,
|
||||
[update.presetId]
|
||||
);
|
||||
if (!exists) {
|
||||
const error = new Error(`Preset ${update.presetId} nicht gefunden.`);
|
||||
error.statusCode = 404;
|
||||
throw error;
|
||||
}
|
||||
const mediaType = String(exists.media_type || '').trim().toLowerCase();
|
||||
const allowedMediaTypes = SLOT_ALLOWED_MEDIA_TYPES[update.slotKey] || new Set(['all']);
|
||||
if (!allowedMediaTypes.has(mediaType)) {
|
||||
const error = new Error(
|
||||
`Preset ${update.presetId} (${mediaType || 'unbekannt'}) ist für ${update.slotKey} nicht zulässig.`
|
||||
);
|
||||
error.statusCode = 400;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await db.exec('BEGIN');
|
||||
try {
|
||||
for (const update of updates) {
|
||||
await db.run(
|
||||
`
|
||||
INSERT INTO user_preset_defaults (slot_key, preset_id)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT(slot_key) DO UPDATE SET preset_id = excluded.preset_id
|
||||
`,
|
||||
[update.slotKey, update.presetId]
|
||||
);
|
||||
}
|
||||
await db.exec('COMMIT');
|
||||
} catch (error) {
|
||||
await db.exec('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
|
||||
logger.info('update', {
|
||||
updates: updates.map((entry) => ({
|
||||
slotKey: entry.slotKey,
|
||||
presetId: entry.presetId
|
||||
}))
|
||||
});
|
||||
return listDefaults();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SLOT_KEYS,
|
||||
listDefaults,
|
||||
updateDefaults
|
||||
};
|
||||
Reference in New Issue
Block a user