Files
cycle-atlas/.agents/skills/faceless-explainer/scripts/lib/host-audio.mjs
T
2026-10-14 21:51:46 +00:00

29 lines
1.4 KiB
JavaScript

// Audio the host app supplied survives every pass that rebuilds audio_meta.json from the storyboard: an entry
// marked "source": "host" whose file exists. A frame the host scored takes no looked-up cue sound.
/** The host's own entries in an earlier audio_meta.json, and why any other host-marked entry was dropped; `exists`
* resolves a path from the project root. A frame written as "1" is frame 1. */
export function hostAudio(previous, exists) {
const marked = [[previous?.bgm, "path"], ...(previous?.sfx ?? []).map((s) => [s, "file"])].filter(
([entry]) => entry?.source === "host",
);
const kept = new Set();
const dropped = [];
for (const [entry, key] of marked) {
const path = entry[key];
if (typeof path !== "string") dropped.push(`a host entry has no "${key}"`);
else if (!exists(path)) dropped.push(`${path} is not on disk`);
else kept.add(entry);
}
const bgm = kept.has(previous?.bgm) ? previous.bgm : null;
const sfx = (previous?.sfx ?? [])
.filter((s) => kept.has(s))
.map((s) => ({ ...s, frame: Number(s.frame) }));
return { bgm, sfx, frames: new Set(sfx.map((s) => s.frame)), dropped };
}
/** `rebuilt` with the host's bed and sounds put back; the host's bed wins over a looked-up one. */
export function keepHostAudio(rebuilt, host) {
const looked = (rebuilt.sfx ?? []).filter((s) => !host.frames.has(s.frame));
return { ...rebuilt, bgm: host.bgm ?? rebuilt.bgm, sfx: [...host.sfx, ...looked] };
}