"use client"; import { useState } from "react"; import { StreamActionLog } from "yt-dlp-transcript-common/components/StreamActionLog"; import { HANDLING_VALUES } from "yt-dlp-transcript-common/lib/channelConfig"; import type { ReplayBucket } from "yt-dlp-transcript-common/jobs/jobSpec"; import { QueueControl } from "../../../components/QueueControl"; import { cancelJobAction } from "../../../jobs/actions"; import { retryBucketAction } from "../pipelineActions"; type Props = { slug: string; ids: string[]; actionLabel: string; defaultQueueKey: string; existingQueues: string[]; // The snapshot bucket these ids represent. When set, the launched retry job // is replayable and re-runs against the current bucket members. bucketKey?: ReplayBucket; // Needs-cookies bucket: force cookie mode "always" for the run and label the // button "Download with cookies" so the manual cookie path is explicit. forceCookies?: boolean; // Auto-captions-only bucket: these videos have a VTT but no audio, so the run // needs BOTH the transcribe handling (a youtube-handling download would fetch // subs, not audio) and the destination-exists bypass. The handling override is // preselected rather than forced, so it stays visible and adjustable. replaceAutoSubs?: boolean; // Overrides the default "Retry (n)" button text. buttonLabel?: string; // Told when a run STARTS here, so a parent card that hides itself on an // empty bucket can stay mounted for this run's log (see below). onRun?: () => void; }; export function RetryBucketControl({ slug, ids, actionLabel, defaultQueueKey, existingQueues, bucketKey, forceCookies, replaceAutoSubs, buttonLabel, onRun, }: Props) { const [queue, setQueue] = useState(defaultQueueKey); const [handlingOverride, setHandlingOverride] = useState( replaceAutoSubs ? "transcribe" : "", ); const [abortOnError, setAbortOnError] = useState(false); // A RETRY EMPTIES ITS OWN BUCKET, and the log must survive that (release 11 // slice O3; plans/FACTS.md, "A run log lives in the panel's React state"). // Every video the run fetches refreshes the snapshot, and the page's refresh // then hands this control an empty `ids` while the run is still streaming — // returning null there unmounted the StreamActionLog and took the log with // it (cookies-mode.spec's intermittent "Retry needs cookies output" not // found). Once a run has started here the control stays, with its button // disabled while the bucket is empty; a reload drops it as before. const [ranHere, setRanHere] = useState(false); if (ids.length === 0 && !ranHere) return null; return (