Simplify CC dropdown: downloaded langs at top, YouTube langs below

Single CC chip shows downloaded langs inline. One click opens a dropdown
with optgroups — already-on-disk at top, YouTube-available below loading
async. No re-download needed to select an already-downloaded lang.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 21:25:41 +02:00
parent da765ce76e
commit 6311b90b21

View File

@@ -671,8 +671,8 @@ export default function Watch() {
const { data: subtitleFiles = [] } = useQuery({ const { data: subtitleFiles = [] } = useQuery({
queryKey: ["subtitle-files", youtubeVideoId], queryKey: ["subtitle-files", youtubeVideoId],
queryFn: () => getSubtitleFiles(youtubeVideoId).then(r => r.data), queryFn: () => getSubtitleFiles(youtubeVideoId).then(r => r.data),
enabled: fileReady, enabled: !!youtubeVideoId,
staleTime: Infinity, staleTime: 60_000,
}); });
const { data: dlStatus } = useQuery({ const { data: dlStatus } = useQuery({
@@ -995,41 +995,22 @@ export default function Watch() {
)} )}
{(() => { {(() => {
// Subs already on disk → show langs + "+" to add more const onDisk = subtitleFiles.map(s => s.lang);
if (subtitleFiles.length > 0 && !subsRequested) return ( const onDiskSet = new Set(onDisk);
<div className="flex items-center gap-1">
<span className="px-3 py-1.5 rounded-full bg-zinc-800 text-zinc-400 text-xs font-mono">
CC: {subtitleFiles.map(s => s.lang).join(", ")}
</span>
<button
onClick={() => setSubsRequested(true)}
className="px-2.5 py-1.5 rounded-full text-xs bg-zinc-800 text-zinc-500 hover:bg-zinc-700 transition-colors"
title="Add subtitle language"
>+</button>
</div>
);
// Not yet asked → show CC chip
if (!subsRequested) return ( if (!subsRequested) return (
<button <button
onClick={() => setSubsRequested(true)} onClick={() => setSubsRequested(true)}
className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-zinc-800 text-zinc-400 hover:bg-zinc-700 transition-colors" className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-zinc-800 text-zinc-400 hover:bg-zinc-700 transition-colors"
title="Check available subtitles on YouTube"
> >
CC CC{onDisk.length > 0 ? ` · ${onDisk.join(", ")}` : ""}
</button> </button>
); );
// Loading YouTube subtitle list
if (subsLoading) return ( const ytManual = (availableSubs?.manual ?? []).filter(l => !onDiskSet.has(l));
<span className="flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-zinc-800 text-zinc-500 text-xs"> const ytAuto = (availableSubs?.auto ?? []).filter(l => !onDiskSet.has(l) && !ytManual.includes(l));
<span className="w-3 h-3 border border-zinc-600 border-t-transparent rounded-full animate-spin inline-block" /> const needsDownload = selectedSubLang && !onDiskSet.has(selectedSubLang);
CC
</span>
);
const manual = new Set(availableSubs?.manual ?? []);
const auto = (availableSubs?.auto ?? []).filter(l => !manual.has(l));
if (!manual.size && !auto.length) return (
<span className="px-3 py-1.5 rounded-full bg-zinc-800 text-zinc-600 text-xs">No CC</span>
);
return ( return (
<> <>
<select <select
@@ -1038,18 +1019,25 @@ export default function Watch() {
className="bg-zinc-800 text-zinc-300 text-xs rounded-full px-3 py-2 border border-zinc-700 focus:outline-none focus:border-accent" className="bg-zinc-800 text-zinc-300 text-xs rounded-full px-3 py-2 border border-zinc-700 focus:outline-none focus:border-accent"
> >
<option value="">No subtitles</option> <option value="">No subtitles</option>
{[...manual].map(l => <option key={l} value={l}>{l}</option>)} {onDisk.length > 0 && (
{auto.map(l => <option key={l} value={l}>{l} (auto)</option>)} <optgroup label="— Downloaded —">
{onDisk.map(l => <option key={l} value={l}>{l}</option>)}
</optgroup>
)}
{(subsLoading || ytManual.length > 0 || ytAuto.length > 0) && (
<optgroup label={subsLoading ? "— Loading YouTube… —" : "— Available on YouTube —"}>
{ytManual.map(l => <option key={l} value={l}>{l}</option>)}
{ytAuto.map(l => <option key={l} value={l}>{l} (auto)</option>)}
</optgroup>
)}
</select> </select>
{dlComplete && selectedSubLang && ( {dlComplete && needsDownload && (
<button <button
onClick={() => addSubsMut.mutate()} onClick={() => addSubsMut.mutate()}
disabled={addSubsMut.isPending} disabled={addSubsMut.isPending}
className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-accent text-black hover:bg-yellow-300 transition-colors disabled:opacity-50" className="flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-medium bg-accent text-black hover:bg-yellow-300 transition-colors disabled:opacity-50"
> >
{addSubsMut.isPending ? ( {addSubsMut.isPending && <span className="w-3 h-3 border-2 border-black/40 border-t-transparent rounded-full animate-spin inline-block" />}
<span className="w-3 h-3 border-2 border-black/40 border-t-transparent rounded-full animate-spin inline-block" />
) : null}
{addSubsMut.isPending ? "Fetching…" : "Add subtitles"} {addSubsMut.isPending ? "Fetching…" : "Add subtitles"}
</button> </button>
)} )}