Files
ferdi2goandClaude Opus 5.5 72e4b8ab7a Einstellungen und Zusatzdateien für den Schnitt ablegen
Intro, Outro, Backing Track und Voice-overs pro Clip laufen über
denselben Upload in Stücken wie die Clips. Die Schnitt-Einstellungen
werden feldweise geprüft; ffprobe akzeptiert jetzt auch reine Tonspuren.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 10:48:53 +00:00

77 lines
2.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { badRequest } from './http-error.js';
export const TARGET_SECONDS = 10;
export function defaultSettings() {
return {
intro: { enabled: false, volume: 100 },
outro: { enabled: false, volume: 100 },
speedUp: true,
muteClips: true,
clipVolume: 100,
music: { enabled: false, volume: 60 },
voiceVolume: 100,
// Pro Clip-Index: Lautstärke des Voice-overs, 0–100
voiceVolumes: {},
// Clip-Indizes, die nicht ins Video sollen
excluded: [],
};
}
function volume(value, name) {
if (!Number.isInteger(value) || value < 0 || value > 100) {
throw badRequest(`${name}: Lautstärke muss zwischen 0 und 100 liegen.`);
}
return value;
}
function flag(value, name) {
if (typeof value !== 'boolean') throw badRequest(`${name}: ungültiger Wert.`);
return value;
}
function clipIndex(value, clipCount) {
const index = Number(value);
if (!Number.isInteger(index) || index < 0 || index >= clipCount) {
throw badRequest('Unbekannter Clip.');
}
return index;
}
// Übernimmt nur bekannte Felder aus patch und prüft jedes; der Rest bleibt.
export function mergeSettings(current, patch, clipCount) {
if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
throw badRequest('Ungültige Einstellungen.');
}
const next = structuredClone({ ...defaultSettings(), ...current });
for (const part of ['intro', 'outro', 'music']) {
if (patch[part] === undefined) continue;
const value = patch[part];
if (!value || typeof value !== 'object') throw badRequest(`${part}: ungültiger Wert.`);
if (value.enabled !== undefined) next[part].enabled = flag(value.enabled, part);
if (value.volume !== undefined) next[part].volume = volume(value.volume, part);
}
for (const key of ['speedUp', 'muteClips']) {
if (patch[key] !== undefined) next[key] = flag(patch[key], key);
}
for (const key of ['clipVolume', 'voiceVolume']) {
if (patch[key] !== undefined) next[key] = volume(patch[key], key);
}
if (patch.voiceVolumes !== undefined) {
if (!patch.voiceVolumes || typeof patch.voiceVolumes !== 'object') {
throw badRequest('voiceVolumes: ungültiger Wert.');
}
for (const [key, value] of Object.entries(patch.voiceVolumes)) {
next.voiceVolumes[clipIndex(key, clipCount)] = volume(value, 'Voice-over');
}
}
if (patch.excluded !== undefined) {
if (!Array.isArray(patch.excluded)) throw badRequest('excluded: ungültiger Wert.');
next.excluded = [...new Set(patch.excluded.map((value) => clipIndex(value, clipCount)))].sort(
(a, b) => a - b,
);
}
return next;
}