import { useEffect, useState } from 'react'; import { Loader2, RefreshCw } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { posterUrl, thumbUrl } from '../thumbnail'; import { useMediaSync } from '../useMediaSync'; const IS_VIDEO = /\.(mp4|mov|webm|m4v)$/i; /** * Which file a media block points at, and whether anything has been made from it yet. * * ── Why "not converted" has to be SAID ── * * Nothing on the site renders an ORIGINAL. Every picture and every video is served from the derived * ladder, so a block pointing at a file the pipeline has not been through renders an empty box — on * the page, in the preview, and here. * * That is a normal thing to hit rather than a mistake: drop a file in, choose it in the same minute, * and you are ahead of a conversion that only starts twenty seconds after the folder goes quiet. An * empty box is indistinguishable from a block saved wrong, so it gets looked for in the wrong place. * * ── Why it reads the sync rather than guessing ── * * "Not converted" and "converting right now" want different things from somebody: the second wants * waiting and the first wants a button. `useMediaSync` already holds both — `current` is what the run * has open this second, and the ledger's `missing`/`stale` are what a run would pick up — so this * says which of the two it is instead of offering one answer for both. * * Reading is free here: the state is a `useGlobal` slot kept current by SyncButton in the action bar, * which is mounted whenever this panel is. Nothing extra is polled by drawing this. * * ── The path is shown whole ── * * From `originals/`, and not truncated. Half these files are called the same thing — every collection * has a hero, every folder a `_1` — so a bare filename identifies one only to somebody who already * knows which it is, and `truncate` cuts the end, which is the wrong half of a path. */ export const MediaBlockSummary = ({ url, onChoose }: { url: string | null; onChoose: () => void }) => { const [missing, setMissing] = useState(false); const { running, state, ledger, busy, start } = useMediaSync(); // Cleared when the block is pointed somewhere else, or the old file's verdict sticks to the new one. useEffect(() => setMissing(false), [url]); const reconciliation = ledger?.reconciliation ?? null; // `current` is what the run has open this second — see SyncState. const converting = Boolean(url) && (state?.current ?? []).includes(url!); const queued = Boolean(url) && (reconciliation?.missing.includes(url!) === true || reconciliation?.stale.includes(url!) === true); return ( <>
Not converted yet. The site serves converted files only, so this block renders nothing until the pipeline has been through it.{' '} {running ? 'A run is going, but this file is not in it — it was picked up after the run started.' : queued ? 'It is outstanding and the next run will take it.' : 'A run starts on its own about twenty seconds after a folder goes quiet. A file renamed or replaced while one was going needs another pass.'}
> )}