feat(organize,web): sidecars follow selected videos; /console/organize?ids= prefill (FE-4 follow-ups)

- planImport (sourcePaths mode): when a selected source is a video file,
  same-directory subtitles / external audio whose stem (language tags
  stripped) matches it join the source list, so the organize wizard, which
  expands a directory into individual videos, no longer drops them.
  Unrelated sidecars stay out; pathGuard applies to the added files.
- Organize records page honours ?ids= (wizard multi-group redirect): shows
  just those records via /files/records, refreshes while running, clear chip.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 16:52:33 +08:00
1 parent 363f85b1a1
commit 2ae780d2a1
5 files changed
+310 -7

No files matched your search

@@ -176,6 +176,38 @@ describe.skipIf(skip)("files 识别 / 整理 / 探测 / 刮削", () => {
await h.db.updateTable("automation_grant").set({ status: "revoked" }).where("id", "=", grant.id).execute();
});
it("organize:选中的视频带上同目录同主干的字幕 / 外挂音轨,无关字幕不带", async () => {
const dir = join(h.dl, "Some.Show.S01.1080p.WEB-DL");
await writeFile(join(dir, "Some.Show.S01E02.1080p.WEB-DL-GRP.chs.ass"), "sub-ep2");
await writeFile(join(dir, "Some.Show.S01E03.1080p.WEB-DL-GRP.mka"), "audio-ep3");
await writeFile(join(dir, "unrelated.srt"), "noise");
// 整理向导把目录展开成逐个视频后提交
const items = ["Some.Show.S01E02.1080p.WEB-DL-GRP.mkv", "Some.Show.S01E03.1080p.WEB-DL-GRP.mkv"].map((n) => ({
root: h.dlRoot,
path: `Some.Show.S01.1080p.WEB-DL/${n}`,
mediaEntityId: show,
season: 1,
}));
const dry = await h.api("POST", "/files/organize", {
idempotencyKey: key(),
items,
action: "copy",
dryRun: true,
minSizeBytes: 0,
});
expect(dry.status).toBe(200);
const g = dry.body.data.groups[0];
expect(g.error).toBeNull();
const byKind = (k: string) =>
g.files.filter((f: { kind: string }) => f.kind === k).map((f: { source: string; target: string }) => f);
expect(byKind("subtitle")).toHaveLength(1);
expect(byKind("subtitle")[0].source).toBe(join(dir, "Some.Show.S01E02.1080p.WEB-DL-GRP.chs.ass"));
expect(byKind("subtitle")[0].target).toMatch(/S01E02.*\.chi\.zh-cn\.ass$/);
expect(byKind("audio")).toHaveLength(1);
expect(byKind("audio")[0].target).toMatch(/S01E03.*\.mka$/);
expect(JSON.stringify(g)).not.toContain("unrelated.srt");
});
it("organize:路径逃逸同样拒绝", async () => {
const r = await h.api("POST", "/files/organize", {
idempotencyKey: key(),