feat(web): 刮削设置分区、作品详情刮削状态与文件管理「刮削」(SCR-1)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 22:36:14 +08:00
1 parent f38eca419e
commit df033130f5
10 files changed
+313 -23

No files matched your search

+19 -2
View File
@@ -9,6 +9,7 @@ import {
Download,
Eye,
EyeOff,
FileDown,
FileStack,
FolderInput,
FolderOpen,
@@ -108,7 +109,7 @@ type Dialog =
| { kind: "delete"; items: FileRef[] }
| { kind: "mkdir" }
| { kind: "copyPath"; entry: FileEntry }
| { kind: "refresh"; items: FileEntry[] }
| { kind: "refresh"; items: FileEntry[]; write?: boolean }
| { kind: "wizard"; source: OrganizeSource }
| { kind: "shortcuts" };
@@ -131,6 +132,7 @@ const MENU_ICON: Record<MenuKey, typeof FolderOpen> = {
move: FolderInput,
copy: Copy,
scrape: RefreshCw,
writeNfo: FileDown,
probe: ScanSearch,
copyPath: ClipboardCopy,
openMedia: Library,
@@ -456,6 +458,8 @@ export default function FilesPage() {
return setDialog({ kind: "transfer", items: refs(list), mode: op });
case "scrape":
return setDialog({ kind: "refresh", items: list });
case "writeNfo":
return setDialog({ kind: "refresh", items: list, write: true });
case "probe":
return doProbe(list);
case "copyPath":
@@ -703,6 +707,13 @@ export default function FilesPage() {
disabled: selected.every((e) => e.asset) ? null : "只对已入库的文件",
onSelect: () => act("scrape", selected),
});
items.push({
key: "writeNfo",
label: "刮削",
icon: FileDown,
disabled: selected.every((e) => e.asset) ? null : "只对已入库的文件",
onSelect: () => act("writeNfo", selected),
});
items.push({ key: "copyPath", label: "复制路径", icon: ClipboardCopy, onSelect: () => void copyPaths(selected) });
return items;
};
@@ -1140,6 +1151,12 @@ export default function FilesPage() {
刷新元数据
</Button>
) : null}
{detail.asset ? (
<Button size="sm" variant="outline" onClick={() => act("writeNfo", [detail])}>
<FileDown />
刮削
</Button>
) : null}
<Button
variant="outline"
size="sm"
@@ -1207,7 +1224,7 @@ export default function FilesPage() {
<CopyPathDialog root={root} entry={dialog.entry} onClose={() => setDialog(null)} />
) : null}
{dialog?.kind === "refresh" ? (
<RefreshMetadataDialog items={dialog.items} onClose={() => setDialog(null)} />
<RefreshMetadataDialog items={dialog.items} write={dialog.write} onClose={() => setDialog(null)} />
) : null}
{dialog?.kind === "wizard" ? <OrganizeWizard source={dialog.source} onClose={() => setDialog(null)} /> : null}
<ShortcutsDialog open={dialog?.kind === "shortcuts"} onOpenChange={(o) => !o && setDialog(null)} />
@@ -63,6 +63,7 @@ import {
VERIFY_STATUS,
} from "~/components/library/labels.ts";
import { Chip, ChipList, Panel, ProgressLine, posterPathOf, relTime, Tone } from "~/components/library/parts.tsx";
import { ScrapePanel } from "~/components/library/scrape-panel.tsx";
import { SlotCard } from "~/components/library/slot-card.tsx";
import { finalOf, UnitFieldDialog } from "~/components/library/unit-field-dialog.tsx";
import {
@@ -1185,6 +1186,7 @@ export default function LibraryDetailPage() {
<MetadataPanel detail={d} entity={entity.data} />
</div>
<PathsPanel detail={d} />
<ScrapePanel detail={d} />
{e.kind !== "movie" || (entity.data?.contentUnits.length ?? 0) > 0 ? (
<UnitsPanel entity={entity} detail={d} />
) : null}