Archilyzer · Source

archilyzer

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

commit 92f4f51e85de625d7bd9911dc7353d3d5e62e52f
parent 9a0daa6ce88dd3f26bc29ea48740b50ff768c19c
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Thu, 17 Sep 2026 20:45:23 -0400

storage: the badge names the drive, not the path

A relocated channel's badge said "Media relocated to /run/media/user/09b598d2-b765-40bd-9466-06d8e16cb119/archilyzer-media" — a true sentence nobody reads. Now that the roots have names, it says "on Platter", and in a table cell that is the whole badge: "on Platter", or "on Platter — unreachable".

The name is a PURE PREFIX MATCH (locationLabelOfDataDir, new beside locationOfDataDir) against the configured roots, resolved by the two server pages that build channel rows. Never a probe: a table draws one badge per row and a probe is up to three subprocesses. Whether the drive is reachable right now is media.status, which inspect() already measured per channel.

The accessible name only EXTENDS: "media location: Media relocated to … · on Platter", so everything that addressed the badge by its old words still finds it.

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

Diffstat:
Mcommon/lib/storageLocations.ts | 20++++++++++++++++++++
Meditor/app/channels/page.tsx | 25++++++++++++++++++-------
Meditor/app/components/MediaLocationBadge.tsx | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Meditor/app/page.tsx | 13++++++++++++-
4 files changed, 108 insertions(+), 14 deletions(-)

diff --git a/common/lib/storageLocations.ts b/common/lib/storageLocations.ts @@ -96,6 +96,26 @@ export function locationOfDataDir( return best; } +// The NAME of the location a channel's media is on, or undefined for none. +// +// The badge's projection, and the reason it lives here rather than beside the +// badge: `MediaLocationBadge.tsx` is imported by `"use client"` files, so it may +// take TYPES from this module but must never call into it — this file imports +// `node:path`, which has no business in a browser bundle. The two server pages +// that build channel rows call this and ship the resulting string. +// +// `label || id` is the same fallback the sanitizer applies on write and +// `views/storage.ts` applies on render: a location whose label was blanked by a +// hand edit is still named by something. +export function locationLabelOfDataDir( + dataDir: string | undefined, + locations: StorageLocation[], +): string | undefined { + if (!dataDir) return undefined; + const found = locationOfDataDir(dataDir, locations); + return found ? found.label || found.id : undefined; +} + // The root of the default location, or "" when there is none. This is the // one-line replacement for every `settings.storage.mediaRoot` read: the Storage // panel's prefill, the bulk move's fallback, the selection deck's box. diff --git a/editor/app/channels/page.tsx b/editor/app/channels/page.tsx @@ -30,7 +30,10 @@ import { getSettings, type SiteSettings, } from "yt-dlp-transcript-common/lib/settings"; -import { defaultLocationRoot } from "yt-dlp-transcript-common/lib/storageLocations"; +import { + defaultLocationRoot, + locationLabelOfDataDir, +} from "yt-dlp-transcript-common/lib/storageLocations"; import { buildChannelBands } from "yt-dlp-transcript-common/views/pipeline/buildBands"; import { EXTERNAL_BAND_IDS } from "yt-dlp-transcript-common/views/pipeline/buildBands"; import { @@ -53,9 +56,10 @@ export const metadata: Metadata = { title: "Channels" }; // makes the page load in milliseconds instead of seconds. That trade is only // honest if the page says how old the numbers are — so report the OLDEST // snapshot on screen, and name the channels that have never had one. -function summariseFreshness( - briefs: ReadonlyArray<ChannelBrief>, -): { oldest: string | null; missing: string[] } { +function summariseFreshness(briefs: ReadonlyArray<ChannelBrief>): { + oldest: string | null; + missing: string[]; +} { let oldest: number | null = null; let oldestIso: string | null = null; const missing: string[] = []; @@ -221,6 +225,15 @@ export default async function ChannelsPage({ status: media.status, target: media.target, detail: media.detail, + // WHICH NAMED LOCATION — a pure prefix match of the recorded + // target against the configured roots, done here because the + // settings are here and the table is a client component. NEVER a + // probe: this table draws one badge per row. Undefined for a root + // nobody named, which renders exactly what it rendered before. + locationLabel: locationLabelOfDataDir( + media.target, + settings.storage.locations, + ), } : null, priority: { @@ -256,9 +269,7 @@ export default async function ChannelsPage({ ? buildChannelGroupSections(activeSite, channels, briefs, settings) : null; const shown = new Set(channels.map((c) => c.slug)); - const freshness = summariseFreshness( - briefs.filter((b) => shown.has(b.slug)), - ); + const freshness = summariseFreshness(briefs.filter((b) => shown.has(b.slug))); return ( // ON md+ THE DOCUMENT STOPS SCROLLING. The page is a flex column exactly // the height of the viewport (main carries py-6, hence -3rem) so that the diff --git a/editor/app/components/MediaLocationBadge.tsx b/editor/app/components/MediaLocationBadge.tsx @@ -35,7 +35,12 @@ import type { export type MediaBadgeInput = Pick< ChannelMediaLocation, "status" | "target" | "detail" ->; +> & { + // The name of the storage location this channel's media is on, projected by + // the server that built the row (see `mediaBadgeOf`). A fourth string is + // still cheaper than shipping the location list to every table. + locationLabel?: string; +}; export type MediaBadgeTone = "neutral" | "danger"; @@ -43,6 +48,10 @@ export type MediaBadge = { label: string; title: string; tone: MediaBadgeTone; + // What a table cell draws. Named here rather than recomputed at the call site + // so the accessible name (always the full `label`) and the visible text + // cannot drift apart. + short: string; }; const LABELS: Record<ChannelMediaStatus, string | null> = { @@ -53,17 +62,54 @@ const LABELS: Record<ChannelMediaStatus, string | null> = { inconsistent: "Media inconsistent", }; +// The one-word state, for the compact rendering of a NAMED location: "on +// Platter — unreachable" says more in less room than "Media unreachable" plus a +// hover, because the name is the half the operator already recognises. +const SHORT_STATUS: Record<ChannelMediaStatus, string | null> = { + "in-place": null, + ok: null, + unreachable: "unreachable", + "in-transition": "moving", + inconsistent: "inconsistent", +}; + // Null means "draw nothing" — an in-place channel, or no location at all (a // caller that could not inspect). Both are the same instruction to a renderer. +// +// `locationLabel` IS THE NAME OF THE STORAGE LOCATION the channel's media sits +// on — `locationOfDataDir(config.dataDir, settings.storage.locations)`, resolved +// on the SERVER where the settings are, and passed down as a string. Undefined +// when it sits on none: a root the operator typed by hand, or a corpus with no +// locations configured. +// +// IT ONLY EVER ADDS. The label keeps its existing words and gains " · on +// Platter", so everything that addresses this badge by `media location: Media +// relocated…` still finds it, while the row now names the drive in the +// operator's own vocabulary instead of an absolute path. +// +// NEVER A PROBE. A table draws dozens of these and a probe is up to three +// subprocesses; the name comes from a pure prefix match against the configured +// roots. Whether that drive is reachable *right now* is `media.status`, which +// inspect() already measured per channel — /storage is where a location's own +// availability is reported. export function mediaBadgeOf( media: MediaBadgeInput | null | undefined, + locationLabel?: string, ): MediaBadge | null { if (!media) return null; const label = LABELS[media.status] ?? null; if (!label) return null; const where = media.target ? ` to ${media.target}` : ""; + const named = (locationLabel ?? "").trim(); + const on = named ? ` · on ${named}` : ""; + const shortStatus = SHORT_STATUS[media.status]; return { - label: media.status === "ok" ? `${label}${where}` : label, + label: (media.status === "ok" ? `${label}${where}` : label) + on, + short: named + ? `on ${named}${shortStatus ? ` — ${shortStatus}` : ""}` + : media.status === "ok" + ? (LABELS.ok as string) + : label, // The detail carries the reason; the target alone is the fallback so a // location written by an older inspect() still says where it points. title: media.detail ?? `${label}${where}`, @@ -77,18 +123,24 @@ const TONE_CLASS: Record<MediaBadgeTone, string> = { }; // `compact` drops the target from the label: a table cell wants the four-word -// state, and the full path is one hover away on the title. +// state, and the full path is one hover away on the title. With a named +// location that becomes "on Platter" / "on Platter — unreachable", which is +// shorter AND says more. export function MediaLocationBadge({ media, compact = false, + locationLabel, }: { media: MediaBadgeInput | null | undefined; compact?: boolean; + locationLabel?: string; }) { - const badge = mediaBadgeOf(media); + // The explicit prop wins, for the Storage panel — it holds a whole + // ChannelMediaLocation from inspect(), which has no room for a name. A table + // row carries the name inside its projected `media` instead. + const badge = mediaBadgeOf(media, locationLabel ?? media?.locationLabel); if (!badge) return null; - const label = - compact && media?.status === "ok" ? (LABELS.ok as string) : badge.label; + const label = compact ? badge.short : badge.label; return ( <span title={badge.title} diff --git a/editor/app/page.tsx b/editor/app/page.tsx @@ -3,6 +3,7 @@ import type { Metadata } from "next"; import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { inspectChannelMedia } from "yt-dlp-transcript-common/lib/channelMedia"; import { getSettings } from "yt-dlp-transcript-common/lib/settings"; +import { locationLabelOfDataDir } from "yt-dlp-transcript-common/lib/storageLocations"; import { getSite, listSiteIds, @@ -84,6 +85,8 @@ export default async function Dashboard({ ), ), ); + // Read once for the whole table, not once per row. + const locations = getSettings().storage.locations; const channels: DashboardChannel[] = rows.map((r) => ({ slug: r.channel.slug, handling: r.channel.config.handling, @@ -98,7 +101,15 @@ export default async function Dashboard({ media: (() => { const m = mediaBySlug.get(r.channel.slug); return m && m.status !== "in-place" - ? { status: m.status, target: m.target, detail: m.detail } + ? { + status: m.status, + target: m.target, + detail: m.detail, + // The named location, matched against the configured roots here + // because the settings are here and the table is a client + // component. A pure prefix match, never a probe. + locationLabel: locationLabelOfDataDir(m.target, locations), + } : null; })(), }));