Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit f0db76164ba11789ac3a589232803a83576b1bfa
parent 87cf6431b0597a1337bca77ffe95947d2e31b8d5
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Mon,  5 Oct 2026 18:59:23 -0400

Merge media/clip-past-end (prepare and compose share the clamped cited span)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Diffstat:
Mcommon/lib/evidenceClip-server.ts | 20+++++++++++++++++++-
Mcommon/publish/composeReports.ts | 6+++---
Mcommon/publish/reportMedia.ts | 14++------------
3 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/common/lib/evidenceClip-server.ts b/common/lib/evidenceClip-server.ts @@ -47,7 +47,7 @@ import { mkdir, readFile, rename, rm, stat } from "node:fs/promises"; import path from "node:path"; import { execa } from "execa"; import { PUBLISH_MAX_FILE_BYTES } from "./builtExport"; -import { tmpPathFor, writeJsonAtomic } from "./jsonFile-server"; +import { readJsonFile, tmpPathFor, writeJsonAtomic } from "./jsonFile-server"; import { roundMomentSeconds } from "./citations/moments"; import type { CitationPad } from "./citations/schema"; import { @@ -91,6 +91,24 @@ export function clampSpanToDuration(span: EvidenceSpan, duration: number | null return { from: span.from, to: Number(duration.toFixed(3)) }; } +// A record's duration (its metadata.info.json), or null when it is not recorded. +export async function recordDuration(channelsDir: string, slug: string, id: string): Promise<number | null> { + const read = await readJsonFile(path.join(channelsDir, slug, "data", id, "metadata.info.json")); + const d = read.ok ? Number((read.value as { duration?: unknown } | null)?.duration) : NaN; + return Number.isFinite(d) && d > 0 ? d : null; +} + +// The span a cited moment's clip is cut for: `evidenceSpan`, its end clamped to +// the record's duration. Prepare and compose both use it, so they agree. +export async function citedEvidenceSpan( + channelsDir: string, + slug: string, + id: string, + c: { start: number; end: number; pad?: CitationPad }, +): Promise<EvidenceSpan> { + return clampSpanToDuration(evidenceSpan(c), await recordDuration(channelsDir, slug, id)); +} + // The span a citation's clip covers: its moment's (rounded) start and end, // widened by its pad, the start clamped at 0. Rounded to the millisecond so a // float's last digits never change a hash. diff --git a/common/publish/composeReports.ts b/common/publish/composeReports.ts @@ -112,7 +112,7 @@ import { type ReportDownloads, type ReportPageView, } from "../lib/report/views"; -import { evidenceSpan, isAudioOnlyPlatform, type EvidenceSpan } from "../lib/evidenceClip-server"; +import { citedEvidenceSpan, isAudioOnlyPlatform, type EvidenceSpan } from "../lib/evidenceClip-server"; import { citedMoments, loadSiteReports, @@ -594,7 +594,7 @@ export async function resolveSiteReports(opts: ResolveSiteReportsOptions): Promi // ship the old cut under the new moment's page. const sidecar = await readJsonFile(path.join(cacheDir, entry.file.replace(/\.[^.]+$/, ".json"))); const cut = sidecar.ok ? (sidecar.value as { span?: EvidenceSpan }).span : undefined; - const want = evidenceSpan({ start: m.moment.start, end: m.moment.end, pad: m.pad }); + const want = await citedEvidenceSpan(paths.channelsDir, m.moment.channel, m.moment.id, { start: m.moment.start, end: m.moment.end, pad: m.pad }); if (cut && !sameSpan(cut, want)) { missing("stale-media", `the clip was cut for ${cut.from}–${cut.to} s, the reports now cite ${want.from}–${want.to} s — prepare again`); continue; @@ -737,7 +737,7 @@ export async function resolveSiteReports(opts: ResolveSiteReportsOptions): Promi } const m = parseMomentKey(key) as SpanMoment; const info = momentInfo.get(key)!; - const span = evidenceSpan({ start: m.start, end: m.end, pad: info.pad }); + const span = await citedEvidenceSpan(paths.channelsDir, m.channel, m.id, { start: m.start, end: m.end, pad: info.pad }); // The transcript the moment's first citation matched best (see the // verification above), else the record's default cues. const cues = momentCues.get(key) ?? records.get(`${c.channel}/${c.id}`)!.cues; diff --git a/common/publish/reportMedia.ts b/common/publish/reportMedia.ts @@ -56,8 +56,7 @@ import type { CitationPad } from "../lib/citations/schema"; import { parseReport } from "../lib/report/validate"; import type { Report } from "../lib/report/schema"; import { - clampSpanToDuration, - evidenceSpan, + citedEvidenceSpan, isAudioOnlyPlatform, prepareEvidenceClip, widerPad, @@ -259,10 +258,7 @@ export async function prepareReportMedia(opts: PrepareReportMediaOptions): Promi continue; } if (m.moment.kind !== "span") continue; - const span = clampSpanToDuration( - evidenceSpan({ start: m.moment.start, end: m.moment.end, pad: m.pad }), - await recordDuration(paths.channelsDir, slug, m.moment.id), - ); + const span = await citedEvidenceSpan(paths.channelsDir, slug, m.moment.id, { start: m.moment.start, end: m.moment.end, pad: m.pad }); const r = await prepareEvidenceClip({ channelsDir: paths.channelsDir, slug, @@ -373,9 +369,3 @@ export async function readReportMediaIndex(paths: Paths, siteId: string): Promis return v as ReportMediaIndex; } -// A record's duration (metadata.info.json), or null when it is not recorded. -async function recordDuration(channelsDir: string, slug: string, id: string): Promise<number | null> { - const read = await readJsonFile(path.join(channelsDir, slug, "data", id, "metadata.info.json")); - const d = read.ok ? Number((read.value as { duration?: unknown } | null)?.duration) : NaN; - return Number.isFinite(d) && d > 0 ? d : null; -}