commit b070be886ce6454d48a9255d8becc1f2d4956615
parent 5c36c9d3ebf17181a79a867237ae648f37ca3a16
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Thu, 1 Oct 2026 13:47:42 -0400
umtool: an export with no chapters.ffmeta reads a deck cut's offsets from its schedule.json, holds included
The segment files alone do not know a clip is held, so the fallback that sums
their lengths is wrong for a cut with posts; the deck's schedule is the cut's
own clock.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Diffstat:
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/umtool/lib/report/export.mjs b/umtool/lib/report/export.mjs
@@ -64,7 +64,7 @@ const titleOf = (e, i) => e.chapter ?? (e.type === "clip" ? `${i + 1}. ${e.video
/**
* Deliverable-second offsets for every entry of the chosen variant.
- * @returns {Promise<{ starts: number[], source: "ffmeta"|"segments", file: string, note?: string } | { error: string }>}
+ * @returns {Promise<{ starts: number[], source: "ffmeta"|"schedule"|"segments", file: string, note?: string } | { error: string }>}
*/
export async function chapterOffsets(dir, manifest, variant) {
const entries = manifest.timeline ?? [];
@@ -84,6 +84,21 @@ export async function chapterOffsets(dir, manifest, variant) {
return { starts: chapters.map((c) => c.start), source: "ffmeta", file };
}
+ // No ffmeta, but a deck schedule for this cut: its starts are the cut's own,
+ // holds included -- the segment files alone do not know a clip is held.
+ const sched = await readFile(path.join(outDir, variant, "schedule.json"), "utf8")
+ .then((t) => JSON.parse(t), () => null);
+ if (sched?.kind === "deck" && Array.isArray(sched.segments) &&
+ sched.segments.map((x) => x.id).join("\n") === entries.map((e) => e.id).join("\n")) {
+ const D = sched.transition ?? 0;
+ return {
+ starts: sched.segments.map((x, i) => (i === 0 ? 0 : x.start + D)),
+ source: "schedule",
+ file: path.join(outDir, variant, "schedule.json"),
+ note: "no chapters.ffmeta; offsets from the deck's schedule.json",
+ };
+ }
+
// No ffmeta: the pipeline's own offset arithmetic over the segments on disk.
const segDirs = [path.join(outDir, variant, "segments"), path.join(outDir, "segments")];
for (const segDir of segDirs) {