feat(web): 刮削设置与对话框加「更新」覆盖方式,内容没变的跳过不逐条列出

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-03 21:04:57 +08:00
1 parent 37cf0a398e
commit 4306bff6d3
4 files changed
+15 -7

No files matched your search

+3 -2
View File
@@ -1093,8 +1093,8 @@ const SKIP_TEXT: Record<string, string> = {
export function ScrapeDialog({ items, root, onClose }: { items: FileEntry[]; root: FileRoot; onClose: () => void }) {
const settings = useSettings();
const scrape = useScrape();
const [mode, setMode] = useState<"missing" | "overwrite" | null>(null);
const curMode = mode ?? settings.data?.organize.scrapeMode ?? "missing";
const [mode, setMode] = useState<"missing" | "overwrite" | "update" | null>(null);
const curMode = mode ?? settings.data?.organize.scrapeMode ?? "update";
const [recordIds, setRecordIds] = useState<string[]>([]);
const [choices, setChoices] = useState<FilesScrapeNeedsChoice[]>([]);
const [picked, setPicked] = useState<Record<string, WizardEntity | null>>({});
@@ -1157,6 +1157,7 @@ export function ScrapeDialog({ items, root, onClose }: { items: FileEntry[]; roo
<fieldset aria-label="已有文件" className="m-0 flex flex-wrap gap-1.5 border-0 p-0">
{(
[
["update", "更新"],
["missing", "只补缺失"],
["overwrite", "全部覆盖"],
] as const
+1 -1
View File
@@ -704,7 +704,7 @@ let seq = 0;
const SCRAPES = new Map<string, ScrapeRecord>();
function mockScrape(b: {
items: { root: string; path: string; mediaEntityId?: string; external?: { externalId: string } }[];
mode?: "missing" | "overwrite";
mode?: "missing" | "overwrite" | "update";
}) {
const records: ScrapeRecord[] = [];
const needsChoice: unknown[] = [];
@@ -70,7 +70,7 @@ function FileRow({ f }: { f: ScrapeFileResult }) {
)}
</span>
<span className="text-meta text-muted-foreground">{ROLE[f.role] ?? f.role}</span>
{f.action !== "written" && f.reason && f.reason !== "scrape_exists" ? (
{f.action !== "written" && f.reason && !QUIET_SKIPS.has(f.reason) ? (
<span className={f.action === "failed" ? "text-meta text-destructive" : "text-meta text-muted-foreground"}>
{reasonLine(f.reason, "写入失败")}
</span>
@@ -80,11 +80,14 @@ function FileRow({ f }: { f: ScrapeFileResult }) {
);
}
/** 不必逐条列出的跳过:已存在(不是本工具写的 / 只补缺失)、内容没变 */
const QUIET_SKIPS = new Set(["scrape_exists", "scrape_unchanged"]);
/** 一条刮削记录:状态、目录与识别结果、计数、逐文件结果(缺省不列"已存在"跳过的) */
export function ScrapeRecordView({ record: s, showPath = false }: { record: ScrapeRecord; showPath?: boolean }) {
const [showAll, setShowAll] = useState(false);
const files = s.files;
const important = files.filter((f) => f.action !== "skipped" || f.reason !== "scrape_exists");
const important = files.filter((f) => f.action !== "skipped" || !QUIET_SKIPS.has(f.reason ?? ""));
const shown = showAll ? files : important.slice(0, 12);
const hidden = files.length - shown.length;
const done = s.status !== "queued" && s.status !== "running";
+6 -2
View File
@@ -57,10 +57,11 @@ const SCRAPE_CARD: CardSpec = {
label: "已有文件",
kind: "select",
options: [
{ value: "update", label: "更新" },
{ value: "missing", label: "只补缺失" },
{ value: "overwrite", label: "全部覆盖" },
],
hint: "全部覆盖会替换目录里已有的 NFO 和图片,包括其他工具写的",
hint: "更新只替换本工具写过、之后没被改过的文件;全部覆盖会替换目录里已有的 NFO 和图片,包括其他工具写的",
},
{
name: "organize.scrapeImages",
@@ -84,13 +85,16 @@ const SCRAPE_CARD: CardSpec = {
toPatch: (v) => ({
organize: {
scrapeAfterImport: v["organize.scrapeAfterImport"] === true,
scrapeMode: str(v["organize.scrapeMode"]) === "overwrite" ? "overwrite" : "missing",
scrapeMode: scrapeModeOf(str(v["organize.scrapeMode"])),
scrapeImages: v["organize.scrapeImages"] === true,
scrapeEpisodeThumbs: v["organize.scrapeEpisodeThumbs"] === true,
},
}),
};
const scrapeModeOf = (v: string | null | undefined): "missing" | "overwrite" | "update" =>
v === "overwrite" || v === "missing" ? v : "update";
export function LibrarySection() {
return (
<div className="grid grid-cols-1 gap-8">