commit 08ae4c72167a60944a87b1bad345d05feb696511
parent 1413de3744febdae605ef2ccd9ea73474e3a5d64
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date: Mon, 21 Sep 2026 02:15:15 -0400
storage: show what the clip windows are costing
The bytes now exist (previous commit); these are the two surfaces that read
them. `LocationRollup` and `StorageRow` gain `clipsBytes` — the "of which" of
the Media figure, rendered UNDER it and never beside it as a second total,
because a window lives inside `data/<id>/` and is already in the number above.
Zero is not rendered. "0 B in clip windows" on every drive that has never been
walked by a report is noise on the one page where a byte figure is supposed to
mean something.
No `unknownClipsBytes`: it would be exactly the set of channels `unknownBytes`
already counts — the two fields are written by one snapshot pass — and a second
copy of one number is a second thing to keep in step.
On the video page the total is free: every window `listClipWindows` returns
already carries its own `bytes`. It goes in the card SUMMARY as well as the
body, and the body says the part that matters — nothing prunes a window on its
own (the retention sweep is pointer-driven and never sees one), and the only
thing that removes one evicts BY AGE.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Diffstat:
8 files changed, 135 insertions(+), 12 deletions(-)
diff --git a/common/controller/storageLocations.ts b/common/controller/storageLocations.ts
@@ -95,6 +95,16 @@ export type LocationRollup = {
// surface renders "+ n unknown" beside the total.
bytes: number;
unknownBytes: number;
+ // THE `clips/` SHARE OF `bytes`, not a sibling of it. Fetched clip windows
+ // live under `data/<id>/clips/` and are counted into `totalMediaBytes` like
+ // everything else there — this is the "of which", because clips are the one
+ // part of a location's bytes that is a CACHE nothing prunes, and an operator
+ // looking at a full drive wants to know how much of it is that.
+ //
+ // No `unknownClipsBytes`: it would be the same set of channels
+ // `unknownBytes` already counts (the two fields are written by one snapshot
+ // pass), and a second copy of one number is a second thing to keep in step.
+ clipsBytes: number;
// `inspectChannelMedia` status, bucketed into the three numbers the page
// shows. `unreachable` DELIBERATELY ABSORBS `inconsistent`: both mean "this
// channel's media is not readable through its link right now", which is the
@@ -114,6 +124,7 @@ function emptyRollup(locationId: string): LocationRollup {
total: 0,
bytes: 0,
unknownBytes: 0,
+ clipsBytes: 0,
ok: 0,
unreachable: 0,
moving: 0,
@@ -158,6 +169,10 @@ export async function channelsOnLocation(opts: {
// file in one render. A slug that is absent (or maps to undefined) counts
// towards `unknownBytes`, never towards `bytes`.
mediaBytes?: Readonly<Record<string, number | undefined>>;
+ // `snapshot.totalClipsBytes` per slug — the `clips/` share of the above.
+ // Absent for a snapshot written before the field existed, which is the same
+ // set `mediaBytes` is absent for.
+ clipsBytes?: Readonly<Record<string, number | undefined>>;
// The in-place row (see INTERNAL_LOCATION_ID). When true, every channel with
// NO `dataDir` is rolled up under that id alongside the configured ones.
includeInternal?: boolean;
@@ -191,6 +206,8 @@ export async function channelsOnLocation(opts: {
const bytes = opts.mediaBytes?.[slug];
if (typeof bytes === "number") roll.bytes += bytes;
else roll.unknownBytes += 1;
+ const clips = opts.clipsBytes?.[slug];
+ if (typeof clips === "number") roll.clipsBytes += clips;
const media = await inspectChannelMedia(opts.paths, slug, config);
if (media.status === "ok" || media.status === "in-place") roll.ok += 1;
else if (media.status === "in-transition") roll.moving += 1;
diff --git a/common/views/review.test.ts b/common/views/review.test.ts
@@ -17,7 +17,7 @@ test("only auto-paused channels are rows, and they carry where Resume puts them
autoPaused: {
reason: "storage",
since: "2026-09-19T04:05:06.000Z",
- previousTier: "high",
+ previousTier: "low",
},
},
// Paused BY A PERSON. It must not appear: /review is for decisions the
@@ -31,14 +31,14 @@ test("only auto-paused channels are rows, and they carry where Resume puts them
rows.map((r) => r.slug),
["zeta"],
);
- assert.equal(rows[0].previousTier, "high");
+ assert.equal(rows[0].previousTier, "low");
assert.equal(rows[0].reason, "storage");
assert.match(rows[0].detail, /drive that is not there/);
assert.match(rows[0].detail, /2026-09-19/);
});
test("the order is the slug order, so an auto-refresh cannot reshuffle the list", () => {
- const entry = (previousTier: "normal" | "high") => ({
+ const entry = (previousTier: "normal" | "low") => ({
tier: "paused" as const,
autoPaused: {
reason: "storage" as const,
@@ -47,7 +47,7 @@ test("the order is the slug order, so an auto-refresh cannot reshuffle the list"
},
});
const rows = autoPausedRows(
- model({ zeta: entry("high"), alpha: entry("normal"), mid: entry("normal") }),
+ model({ zeta: entry("low"), alpha: entry("normal"), mid: entry("normal") }),
);
assert.deepEqual(
rows.map((r) => r.slug),
diff --git a/common/views/storage.test.ts b/common/views/storage.test.ts
@@ -3,7 +3,12 @@ import assert from "node:assert/strict";
import type { StorageLocation } from "../lib/storageLocations";
import type { LocationRollup, MemoizedProbe } from "../controller/storageLocations";
import type { RegistryReader } from "./inputs";
-import { buildStorageRows, type StorageActionKind, type StorageRow } from "./storage";
+import {
+ buildStorageRows,
+ storageClipsText,
+ type StorageActionKind,
+ type StorageRow,
+} from "./storage";
// Run with:
// pnpm --filter yt-dlp-transcript-common exec tsx --test views/storage.test.ts
@@ -25,6 +30,7 @@ function rollup(partial: Partial<LocationRollup>): LocationRollup {
total: partial.total ?? 0,
bytes: partial.bytes ?? 0,
unknownBytes: partial.unknownBytes ?? 0,
+ clipsBytes: partial.clipsBytes ?? 0,
ok: partial.ok ?? 0,
unreachable: partial.unreachable ?? 0,
moving: partial.moving ?? 0,
@@ -359,3 +365,37 @@ test("no internal input means no internal row (a caller that wants only the loca
);
assert.equal(bytesInPlace, 0);
});
+
+test("the clip-window line is rendered only when there are windows", () => {
+ // Zero is not rendered: "0 B in clip windows" on every drive that has never
+ // been walked by a report is noise on the one page where a byte figure is
+ // supposed to mean something.
+ assert.equal(storageClipsText(0), "");
+ assert.equal(storageClipsText(-1), "");
+ assert.match(storageClipsText(5 * 1024 ** 3), /5\.00 GB of it is fetched/);
+});
+
+test("a location row carries the clips share of its bytes", () => {
+ const payload = buildStorageRows({
+ locations: [loc("cold", "/mnt/cold")],
+ defaultLocationId: "",
+ probes: {},
+ rollups: {
+ cold: rollup({
+ locationId: "cold",
+ total: 2,
+ bytes: 10_000_000_000,
+ clipsBytes: 2_000_000_000,
+ ok: 2,
+ }),
+ },
+ registry: NO_JOBS,
+ udisksctlAvailable: false,
+ now: 0,
+ });
+ const row = payload.rows.find((r) => r.id === "cold");
+ assert.equal(row?.clipsBytes, 2_000_000_000);
+ assert.match(String(row?.clipsText), /fetched clip windows/);
+ // A SUBSET, not a sibling: the clips are already inside `bytes`.
+ assert.equal(row?.bytes, 10_000_000_000);
+});
diff --git a/common/views/storage.ts b/common/views/storage.ts
@@ -88,6 +88,15 @@ export type StorageRow = {
// ONE wording, and never a bare "0 B" for a location whose channels have
// simply never had a report — that reads as an empty drive.
bytesText: string;
+ // The `clips/` share of `bytes` — fetched clip windows, the one part of a
+ // location's media that is a cache nothing prunes. 0 is a real answer here
+ // (no windows have been fetched onto this drive), unlike `bytes`, which is
+ // why it needs no "unknown" companion: the channels that could not measure
+ // clips are exactly the ones `unknownBytes` already counts.
+ clipsBytes: number;
+ // "· 4.21 GB in clip windows", or "" when there are none. Built here so the
+ // row and any future poll cannot word it differently.
+ clipsText: string;
// Absent when the location is not available, and ALSO when it is available
// but unmeasurable (getFreeBytes fails open to Infinity, which the probe
// drops rather than carry). Render "—" for both.
@@ -259,6 +268,18 @@ export function storageBytesText(bytes: number, unknown: number): string {
return bytesLabel(bytes);
}
+// THE CLIP-WINDOW LINE, or nothing at all.
+//
+// Zero is not rendered: a row saying "0 B in clip windows" on every drive that
+// has never been walked by a report is noise on the only page where a byte
+// figure is supposed to mean something. The phrasing names EVICTION rather than
+// size, because the number is only actionable if you know there is a control
+// for it.
+export function storageClipsText(clipsBytes: number): string {
+ if (clipsBytes <= 0) return "";
+ return `${bytesLabel(clipsBytes)} of it is fetched clip windows`;
+}
+
// The one running re-point, or null. Reported for the whole page rather than
// per row because the job record carries no location id: it is enqueued on the
// shared `relocate` queue key, which the registry caps at concurrency 1, so
@@ -331,6 +352,7 @@ export function buildStorageRows(i: StorageRowsInputs): StorageRowsPayload {
const roll = i.rollups[loc.id];
const bytes = roll?.bytes ?? 0;
const unknownBytes = roll?.unknownBytes ?? 0;
+ const clipsBytes = roll?.clipsBytes ?? 0;
bytesOnLocation[loc.id] = bytes;
return {
id: loc.id,
@@ -348,6 +370,8 @@ export function buildStorageRows(i: StorageRowsInputs): StorageRowsPayload {
bytes,
unknownBytes,
bytesText: storageBytesText(bytes, unknownBytes),
+ clipsBytes,
+ clipsText: storageClipsText(clipsBytes),
...(probe?.freeBytes !== undefined ? { freeBytes: probe.freeBytes } : {}),
lastProbeAgeMs: probe ? Math.max(0, i.now - probe.probedAt) : 0,
...(probe?.warning ? { warning: probe.warning } : {}),
@@ -431,6 +455,7 @@ function internalRow(
const counts = countsOf(roll);
const bytes = roll?.bytes ?? 0;
const unknownBytes = roll?.unknownBytes ?? 0;
+ const clipsBytes = roll?.clipsBytes ?? 0;
bytesOnLocation[INTERNAL_ROW_ID] = bytes;
const withheld =
"The corpus volume is where media lives when it has not been moved " +
@@ -452,6 +477,8 @@ function internalRow(
bytes,
unknownBytes,
bytesText: storageBytesText(bytes, unknownBytes),
+ clipsBytes,
+ clipsText: storageClipsText(clipsBytes),
...(internal.freeBytes !== undefined ? { freeBytes: internal.freeBytes } : {}),
lastProbeAgeMs: 0,
busy: null,
diff --git a/editor/app/channels/[slug]/videos/[id]/components/VideoPanel.tsx b/editor/app/channels/[slug]/videos/[id]/components/VideoPanel.tsx
@@ -433,9 +433,16 @@ export function VideoPanel({
<PipelineStageCard
id="fetched-windows"
title="Fetched windows"
+ // THE BYTES ARE IN THE SUMMARY, not only inside the card. A window is
+ // seconds of media, but nothing prunes one — the retention sweep is
+ // pointer-driven and never sees a `clips/` file — so a video walked
+ // by many reports accumulates them silently. The total is free here:
+ // every window in the list already carries its own `bytes`.
summary={`${clipWindows.length} window${
clipWindows.length === 1 ? "" : "s"
- } of this video's source media, fetched for another tool.`}
+ } (${formatBytes(
+ clipWindows.reduce((n, w) => n + w.bytes, 0),
+ )}) of this video's source media, fetched for another tool.`}
defaultOpen={false}
tone="neutral"
>
@@ -1221,6 +1228,16 @@ function FetchedWindows({ windows }: { windows: FetchedWindow[] }) {
metadata, so the video's pipeline state and its place in the index
are unchanged.
</p>
+ {/* WHAT THEY COST, AND THAT NOTHING RECLAIMS IT. The retention sweep is
+ pointer-driven and never sees a window; the cleanup lanes are about
+ `audio.*`. The only thing that removes one is Evict fetched windows on
+ /storage, and it evicts BY AGE — which surfaces have to say, because a
+ window a report still cites looks identical to one nothing wants. */}
+ <p aria-label="clip windows total" className="text-xs text-muted-foreground">
+ {formatBytes(windows.reduce((n, w) => n + w.bytes, 0))} in total,
+ counted in this channel's media size. Nothing prunes a window on
+ its own — Storage evicts them by age.
+ </p>
</div>
);
}
diff --git a/editor/app/storage/buildStorage.ts b/editor/app/storage/buildStorage.ts
@@ -43,7 +43,14 @@ export async function buildStorage(): Promise<StorageRowsPayload> {
// row says so rather than under-reporting.
const briefs = await listChannelBriefs(paths);
const mediaBytes: Record<string, number | undefined> = {};
- for (const b of briefs) mediaBytes[b.slug] = b.snapshot?.totalMediaBytes;
+ // The `clips/` share, off the same snapshot. Absent for a report written
+ // before the field existed — the same set `mediaBytes` is absent for, which
+ // is why the rollup needs no second "unknown" counter.
+ const clipsBytes: Record<string, number | undefined> = {};
+ for (const b of briefs) {
+ mediaBytes[b.slug] = b.snapshot?.totalMediaBytes;
+ clipsBytes[b.slug] = b.snapshot?.totalClipsBytes;
+ }
const configs = briefs.map((b) => ({ slug: b.slug, config: b.config }));
const [probes, rollups, udisksctl, freeOnCorpus] = await Promise.all([
probeAllLocations(locations, paths),
@@ -52,6 +59,7 @@ export async function buildStorage(): Promise<StorageRowsPayload> {
locations,
configs,
mediaBytes,
+ clipsBytes,
includeInternal: true,
}),
// Memoised per binary path inside storageVolumes, so this is one
diff --git a/editor/app/storage/components/StorageLocationsTable.tsx b/editor/app/storage/components/StorageLocationsTable.tsx
@@ -220,7 +220,21 @@ function LocationCard({
</>
)}
<dt className="text-muted-foreground">Media</dt>
- <dd aria-label="location media bytes">{row.bytesText}</dd>
+ <dd aria-label="location media bytes">
+ {row.bytesText}
+ {/* THE CLIPS SHARE, inside the Media figure and never beside it as a
+ second total. A fetched window lives under `data/<id>/clips/`, so
+ it is already in the number above; what this adds is that some of
+ it is a CACHE nothing prunes. Absent when there are none. */}
+ {row.clipsText && (
+ <span
+ aria-label="location clip bytes"
+ className="block text-xs text-muted-foreground"
+ >
+ {row.clipsText}
+ </span>
+ )}
+ </dd>
<dt className="text-muted-foreground">Free</dt>
<dd aria-label="location free space">
{row.freeBytes === undefined ? "—" : formatBytes(row.freeBytes)}
diff --git a/editor/e2e/review.spec.ts b/editor/e2e/review.spec.ts
@@ -53,7 +53,7 @@ test("an auto-paused channel is listed with its reason", async ({ page }) => {
autoPaused: {
reason: "storage",
since: "2026-09-19T04:05:06.000Z",
- previousTier: "high",
+ previousTier: "low",
},
},
},
@@ -74,10 +74,10 @@ test("an auto-paused channel is listed with its reason", async ({ page }) => {
page.getByLabel("auto-paused reason for test-youtube"),
).toContainText("drive that is not there");
// It offers to put the channel back where it WAS, not at "normal" — the
- // previous tier is the whole content of the record, and resuming a `high`
+ // previous tier is the whole content of the record, and resuming a `low`
// channel as `normal` would silently demote it.
const resume = page.getByLabel("resume test-youtube");
- await expect(resume).toHaveText("Resume as high");
+ await expect(resume).toHaveText("Resume as low");
// And the fix for the actual problem is one link away.
await expect(section.getByRole("link", { name: "Storage" })).toHaveAttribute(
@@ -102,7 +102,7 @@ test("an auto-paused channel is listed with its reason", async ({ page }) => {
},
{ timeout: 20_000 },
)
- .toMatchObject({ tier: "high" });
+ .toMatchObject({ tier: "low" });
const after = await readJson<{
channelPriority?: {
channels: Record<string, { autoPaused?: unknown }>;