Archilyzer · Source

archilyzer

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

commit 32b81b9082dd5e1cee5695b54cd547679582f9d8
parent 20466b29e57eab7e474707981b5cb0e68493ef5f
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Fri,  2 Oct 2026 00:47:21 -0400

common: the live-chat archive reads no raw replay while the media is away

A build re-normalizes a stale cues file from the tiered raw replay only while
the channel's media is ok or in place; otherwise it stages the cues on disk as
they are and logs how many are older than their raw replay.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Diffstat:
Mcommon/controller/archiveLiveChat.ts | 48++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 40 insertions(+), 8 deletions(-)

diff --git a/common/controller/archiveLiveChat.ts b/common/controller/archiveLiveChat.ts @@ -26,7 +26,8 @@ import { } from "node:fs/promises"; import pLimit from "p-limit"; import { listChannelStatsFromDisk, type ChannelStat } from "./channels"; -import { normalizeLiveChat } from "./normalizeLiveChat"; +import { isLiveChatCuesFresh, normalizeLiveChat } from "./normalizeLiveChat"; +import { inspectChannelMedia } from "../lib/channelMedia"; import { archiveExtension, archiveSidecarPath, @@ -139,9 +140,22 @@ async function stageChannel( log(`Archive live chat ${ch.slug}: scanning ${videoIds.length} videos`); + // THE RAW REPLAY IS MEDIA (release 17): `transcript.live_chat.json` is a + // tiered link onto the channel's media drive. A build re-normalizes a stale + // cues file from it only while that drive is reachable; otherwise the cues + // the corpus disk holds are staged as they are (the freshness check `lstat`s + // the link, so asking it costs the drive nothing), and a video with no cues + // yet is left out of this build. Never a read of a drive that is unmounted, + // stalled or mid-move. + const media = await inspectChannelMedia(paths, ch.slug, ch.config, { + fresh: true, + }); + const rawReadable = media.status === "ok" || media.status === "in-place"; + let normalizedCount = 0; let linkedCount = 0; let normalizeFailed = 0; + let keptStale = 0; const requiresRewrite = !build.includeMetadata || build.prettyPrint; @@ -157,13 +171,24 @@ async function stageChannel( } let outcome; try { - outcome = await normalizeLiveChat({ - videoDir, - channelSlug: ch.slug, - configName: ch.config.name, - // A build moves no media (release 17): the jobs tier, this reads. - tier: false, - }); + if (rawReadable) { + outcome = await normalizeLiveChat({ + videoDir, + channelSlug: ch.slug, + configName: ch.config.name, + // A build moves no media (release 17): the jobs tier, this reads. + tier: false, + }); + } else { + const { fresh, cuesPath } = await isLiveChatCuesFresh(videoDir); + const have = await stat(cuesPath).then( + (st) => st.isFile(), + () => false, + ); + if (!have) return; + if (!fresh) keptStale++; + outcome = { status: "fresh" as const, cuesPath }; + } } catch (err) { normalizeFailed++; log(` ! normalize ${ch.slug}/${id}: ${(err as Error).message}`); @@ -189,6 +214,13 @@ async function stageChannel( if (normalizeFailed > 0) { log(` ${ch.slug}: ${normalizeFailed} videos failed to normalize`); } + if (!rawReadable) { + log( + ` ${ch.slug}: its media is not reachable (${media.detail ?? media.status}) — ` + + `staged the live-chat cues on disk as they are` + + (keptStale > 0 ? ` (${keptStale} older than their raw replay)` : ""), + ); + } if (linkedCount > 0) { await writeFile(