- 标题区:海报、作品名 (年份)、站点;资源行显示原始标题、促销、H&R、做种 / 下载、大小、发布时间 - 下载器与保存目录并排下拉,缺省「自动」,下方写明「将保存到」的实际路径;保留「自定义路径」 - 作品身份折叠区:类型 + TMDB 编号(可搜索作品),未识别时默认展开,指定后随下载绑定 - 已有版本先显示一行摘要,选一次性洗版才展开勾选列表 - 搜索、工作台、原站视图三处调用方补传资源字段 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
86 lines
3.1 KiB
TypeScript
86 lines
3.1 KiB
TypeScript
import type { BrowserResourceResult, BrowserSessionView } from "@umt/contracts";
|
||
import { Download, Settings2 } from "lucide-react";
|
||
import { useState } from "react";
|
||
import { Link } from "react-router";
|
||
import { toast } from "sonner";
|
||
import { useSubmitRelease } from "~/api/releases.ts";
|
||
import { ManualDownloadDialog } from "~/components/downloads/manual-download-dialog.tsx";
|
||
import { Button } from "~/components/ui/button.tsx";
|
||
import { workbenchResource } from "~/components/workbench/download-dialog.tsx";
|
||
import { apiData, errorMessage } from "~/lib/api.ts";
|
||
|
||
/** 控件位于 iframe/截图外;直接解析当前详情页并交给共同手动下载入口。 */
|
||
export function ResourceDownload({ session }: { session: BrowserSessionView }) {
|
||
const [busy, setBusy] = useState(false);
|
||
const [resource, setResource] = useState<BrowserResourceResult | null>(null);
|
||
const [open, setOpen] = useState(false);
|
||
const [unrecognized, setUnrecognized] = useState(false);
|
||
const [planId, setPlanId] = useState<string | null>(null);
|
||
const submit = useSubmitRelease();
|
||
const prepare = async (options: boolean) => {
|
||
setBusy(true);
|
||
try {
|
||
const r = await apiData<BrowserResourceResult>("/browser/sessions/{id}/resource", {
|
||
method: "POST",
|
||
pathParams: { id: session.id },
|
||
body: {},
|
||
});
|
||
setResource(r);
|
||
setUnrecognized(false);
|
||
if (options) setOpen(true);
|
||
else {
|
||
const result = await submit.mutateAsync({
|
||
sourceReleaseId: r.release.id,
|
||
importMode: "normal",
|
||
replacementAssetIds: [],
|
||
});
|
||
if (result.requiresConfirmation) {
|
||
setUnrecognized(true);
|
||
setOpen(true);
|
||
} else if (result.submitted) {
|
||
setPlanId(result.plan?.id ?? null);
|
||
toast.success("已提交下载");
|
||
} else toast.warning(result.rejected.map((v) => v.message).join(";") || "没有提交下载");
|
||
}
|
||
} catch (e) {
|
||
toast.error(errorMessage(e));
|
||
} finally {
|
||
setBusy(false);
|
||
}
|
||
};
|
||
return (
|
||
<>
|
||
<div className="flex flex-wrap items-center gap-2">
|
||
<Button size="sm" disabled={busy || session.controller === "closed"} onClick={() => void prepare(false)}>
|
||
<Download />
|
||
{busy ? "正在处理…" : "下载当前资源"}
|
||
</Button>
|
||
<Button
|
||
size="sm"
|
||
variant="outline"
|
||
disabled={busy || session.controller === "closed"}
|
||
onClick={() => void prepare(true)}
|
||
>
|
||
<Settings2 />
|
||
下载选项
|
||
</Button>
|
||
{planId ? (
|
||
<Link className="text-caption underline" to={`/console/downloads?plan=${planId}`}>
|
||
查看下载任务
|
||
</Link>
|
||
) : null}
|
||
</div>
|
||
{resource ? (
|
||
<ManualDownloadDialog
|
||
key={resource.release.id}
|
||
resource={workbenchResource(resource.release)}
|
||
open={open}
|
||
onOpenChange={setOpen}
|
||
initialUnrecognized={unrecognized}
|
||
submit={(options) => submit.mutateAsync({ ...options, sourceReleaseId: resource.release.id })}
|
||
/>
|
||
) : null}
|
||
</>
|
||
);
|
||
}
|