Files
ripster/backend/src/services/userPresetDefaultsService.js
T
michael 6fbc5395a1
Deploy Docs to GitHub Pages / Build Documentation (push) Has been cancelled
Deploy Docs to GitHub Pages / Deploy to GitHub Pages (push) Has been cancelled
0.16.1-5 Ebcode Fix / presets
2026-04-30 12:37:32 +00:00

135 lines
3.5 KiB
JavaScript

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
};