feat(downloads): 多下载器路由:默认下载器、站点指定下载器、别名

照 MP(chain/download/submission.py:明确指定 or 站点下载器 or 默认):
- 迁移 0063:downloader_instance.is_default(至多一个,唯一索引)、site.downloader_instance_id
- pickInstance:明确指定 > 站点指定 > 默认 > 第一个可用;自动下载(gates)与手动下载共用
- 设置:下载器名称改称别名,新增「设为默认下载器」;站点编辑新增「下载器」选择
- 测试 downloader-routing.test.ts

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-02 20:32:29 +08:00
1 parent 64e1dfb1d2
commit 135c50048f
12 files changed
+196 -11

No files matched your search

@@ -0,0 +1,63 @@
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { loadInstances, pickInstance } from "../../src/modules/acquisition-organize/services/common.ts";
import { createHarness, type Harness, pgAvailable, seedBase } from "./harness.ts";
const hasPg = await pgAvailable();
// 多下载器路由(照 MP):明确指定 > 站点指定 > 默认 > 第一个可用
describe.skipIf(!hasPg)("下载器选择", () => {
let h: Harness;
let first: string;
let anime: string;
let siteId: string;
beforeAll(async () => {
h = await createHarness("downloader_routing");
const base = await seedBase(h.db, {
libraryPath: "/tmp/lib",
cachePath: "/tmp/cache",
qbUrl: "http://127.0.0.1:9",
});
first = base.instanceId;
siteId = base.siteId;
const add = async (name: string) =>
(
await h.db
.insertInto("downloader_instance")
.values({ name, base_url: "http://127.0.0.1:9", settings: JSON.stringify({ savePath: "/tmp/cache" }) })
.returning("id")
.executeTakeFirstOrThrow()
).id;
anime = await add("zz-番剧专用");
});
afterAll(async () => {
await h?.close();
});
const pick = async (opts: Parameters<typeof pickInstance>[2]) =>
(await pickInstance(h.db, await loadInstances(h.db), opts))?.inst.id;
it("都没指定:第一个可用;设了默认后用默认", async () => {
expect(await pick({})).toBe(first);
await h.db.updateTable("downloader_instance").set({ is_default: true }).where("id", "=", anime).execute();
expect(await pick({ siteId })).toBe(anime);
});
it("站点指定优先于默认;明确指定优先于站点", async () => {
await h.db.updateTable("site").set({ downloader_instance_id: first }).where("id", "=", siteId).execute();
expect(await pick({ siteId })).toBe(first);
expect(await pick({ siteId, explicitId: anime })).toBe(anime);
});
it("站点指定的下载器停用时回落默认;明确指定的不可用时不悄悄换", async () => {
await h.db.updateTable("downloader_instance").set({ enabled: false }).where("id", "=", first).execute();
expect(await pick({ siteId })).toBe(anime);
expect(await pick({ explicitId: first })).toBeUndefined();
});
it("默认下载器至多一个", async () => {
await expect(
h.db.updateTable("downloader_instance").set({ is_default: true }).where("id", "=", first).execute(),
).rejects.toThrow();
});
});