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:
ferdi2goandClaude Opus 5.5 committed 2026-09-29 10:48:53 +00:00
1 parent d7102d04a4
commit 72e4b8ab7a
6 files changed
+340 -45

No files matched your search

+47 -1
View File
@@ -11,7 +11,7 @@ let baseUrl;
let cookie;
let dataDir;
const fakeMedia = { duration: 42, width: 1080, height: 1920, rotation: 0, fps: 30, hasAudio: true };
const fakeMedia = { duration: 42, hasVideo: true, width: 1080, height: 1920, rotation: 0, fps: 30, hasAudio: true };
before(async () => {
dataDir = await fs.mkdtemp(path.join(os.tmpdir(), 'fdc-test-'));
@@ -141,3 +141,49 @@ test('Projekt löschen entfernt die Dateien', async () => {
assert.equal((await call('GET', `projects/${project.id}`)).status, 404);
await assert.rejects(fs.stat(path.join(dataDir, 'projects', project.id)));
});
test('Einstellungen speichern und prüfen', async () => {
const project = await createProject([clip('a.mp4', 5), clip('b.mp4', 5)]);
assert.equal(project.settings.muteClips, true);
let res = await call('PATCH', `projects/${project.id}/settings`, { music: { enabled: true, volume: 40 } });
const updated = await res.json();
assert.deepEqual(updated.settings.music, { enabled: true, volume: 40 });
res = await call('PATCH', `projects/${project.id}/settings`, { excluded: [5] });
assert.equal(res.status, 400);
});
test('Zusatzdateien anmelden, hochladen, ersetzen und löschen', async () => {
const project = await createProject([clip('a.mp4', 5)]);
const base = `projects/${project.id}/assets`;
// Falscher Dateityp für den Platz
assert.equal((await call('POST', `${base}/music`, { file: 'intro.txt', size: 4 })).status, 400);
// Unbekannter Platz und Voice-over zu einem Clip, den es nicht gibt
assert.equal((await call('POST', `${base}/abspann`, { file: 'a.mp3', size: 4 })).status, 404);
assert.equal((await call('POST', `${base}/voice-3`, { file: 'a.webm', size: 4 })).status, 404);
let res = await call('POST', `${base}/voice-0`, { file: 'aufnahme.webm', size: 4 });
assert.equal(res.status, 201);
assert.equal((await res.json()).assets['voice-0'].status, 'pending');
const put = (slot, offset, text) =>
call('PUT', `${base}/${slot}?offset=${offset}`, new TextEncoder().encode(text), {
'Content-Type': 'application/octet-stream',
});
res = await put('voice-0', 0, 'stim');
assert.equal((await res.json()).status, 'ready');
assert.equal(await (await call('GET', `${base}/voice-0/file`)).text(), 'stim');
// Ersetzen verwirft die alte Datei
await call('POST', `${base}/voice-0`, { file: 'neu.m4a', size: 3 });
const replaced = await (await call('GET', `projects/${project.id}`)).json();
assert.equal(replaced.assets['voice-0'].file, 'neu.m4a');
assert.equal(replaced.assets['voice-0'].received, 0);
const files = await fs.readdir(path.join(dataDir, 'projects', project.id, 'assets'));
assert.deepEqual(files, []);
res = await call('DELETE', `${base}/voice-0`);
assert.deepEqual((await res.json()).assets, {});
});
+46
View File
@@ -0,0 +1,46 @@
import assert from 'node:assert/strict';
import { test } from 'node:test';
import { defaultSettings, mergeSettings } from '../server/settings.js';
test('Vorgaben: beschleunigen und stummschalten an, Intro, Outro und Musik aus', () => {
const settings = defaultSettings();
assert.equal(settings.speedUp, true);
assert.equal(settings.muteClips, true);
assert.equal(settings.intro.enabled, false);
assert.equal(settings.music.volume, 60);
});
test('Teiländerungen lassen den Rest stehen', () => {
const merged = mergeSettings(defaultSettings(), { music: { volume: 35 } }, 5);
assert.equal(merged.music.volume, 35);
assert.equal(merged.music.enabled, false);
assert.equal(merged.intro.volume, 100);
});
test('Voice-over-Lautstärke pro Clip und ausgeschlossene Clips', () => {
const merged = mergeSettings(defaultSettings(), { voiceVolumes: { 2: 80 }, excluded: [3, 1, 3] }, 5);
assert.deepEqual(merged.voiceVolumes, { 2: 80 });
assert.deepEqual(merged.excluded, [1, 3]);
});
test('Ungültige Werte werden abgelehnt', () => {
const invalid = [
{ music: { volume: 101 } },
{ clipVolume: -1 },
{ clipVolume: 50.5 },
{ speedUp: 'ja' },
{ voiceVolumes: { 9: 50 } },
{ excluded: [7] },
[],
];
for (const patch of invalid) {
assert.throws(() => mergeSettings(defaultSettings(), patch, 5), { status: 400 });
}
});
test('Unbekannte Felder werden ignoriert', () => {
const merged = mergeSettings(defaultSettings(), { hacker: true, intro: { enabled: true, file: '/etc/passwd' } }, 1);
assert.equal(merged.hacker, undefined);
assert.equal(merged.intro.file, undefined);
assert.equal(merged.intro.enabled, true);
});