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>
This commit is contained in:
1 parent
d7102d04a4
commit
72e4b8ab7a
6 files changed
+340
-45
No files matched your search
@@ -0,0 +1,76 @@
|
||||
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;
|
||||
}
|
||||
Reference in new issue
Block a user