feat(subtitles): 字幕库验证码由智能体视觉接管;字幕站原站视图下载当前字幕

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 11:43:08 +08:00
1 parent 023063edf4
commit c30a758a74
24 files changed
+466 -41

No files matched your search

@@ -6,7 +6,7 @@ import type {
SubtitleWriteView,
} from "@umt/contracts";
import { Download, ExternalLink, FileUp, LoaderCircle, Search, Undo2 } from "lucide-react";
import { useId, useMemo, useRef, useState } from "react";
import { useEffect, useId, useMemo, useRef, useState } from "react";
import { toast } from "sonner";
import {
useApplySubtitles,
@@ -68,11 +68,14 @@ export function SubtitleDialog({
title,
open,
onOpenChange,
fromSession,
}: {
entityId: string;
title: string;
open: boolean;
onOpenChange: (o: boolean) => void;
/** 原站视图会话:打开时自动下载它当前页的字幕并配对 */
fromSession?: string;
}) {
const inputId = useId();
const input = useRef<HTMLInputElement>(null);
@@ -86,7 +89,20 @@ export function SubtitleDialog({
const [dragging, setDragging] = useState(false);
const [tab, setTab] = useState<"upload" | "online">("upload");
const list = targets.data?.targets ?? [];
const busy = parse.isPending || apply.isPending;
const fetchPage = useFetchSubtitle();
const busy = parse.isPending || apply.isPending || fetchPage.isPending;
const fetchedFrom = useRef<string | null>(null);
useEffect(() => {
if (!open || !fromSession || fetchedFrom.current === fromSession) return;
fetchedFrom.current = fromSession;
fetchPage
.mutateAsync({ entityId, sessionId: fromSession })
.then((r) => {
setRows((prev) => [...prev, ...r.items.map((it) => ({ ...it, target: it.targetLocationId ?? SKIP }))]);
setSkipped((prev) => [...prev, ...r.skipped]);
})
.catch((err) => toast.error(errorMessage(err, "下载当前字幕失败")));
}, [open, fromSession, entityId, fetchPage]);
const load = async (files: File[]) => {
if (!files.length) return;
@@ -207,6 +223,12 @@ export function SubtitleDialog({
/>
)}
{fetchPage.isPending ? (
<p className="flex items-center gap-2 text-caption text-muted-foreground">
<LoaderCircle className="size-4 animate-spin" />
正在下载原站视图当前页的字幕…
</p>
) : null}
{rows.length ? (
<ul className="space-y-2">
{rows.map((r) => (