feat(identity): 豆瓣 / bgm.tv 辅助信息源与发现页
- adapters/meta:bgm.tv 官方 API(条目 / 搜索 / 每日放送)与豆瓣移动版接口(条目 / 实时热门) - 识别升级链:豆瓣条目缓存没有时在线查;AniBT 的 bgm ID、公开 BT 站标题搜 bgm → 名称 + 年份匹配 TMDB(季条目去季名与年份) - 条目缓存 30 天,查不到 6 小时负缓存;测试进程缺省不出外网 - 发现页:bgm 每日放送、豆瓣热门电影 / 剧集,点条目匹配 TMDB 作品 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
b8876542b8
commit
709414778e
23 files changed
+958
-31
No files matched your search
@@ -65,6 +65,7 @@ describe.skipIf(!pg)("ID-3 识别升级链:详情页 ID", () => {
|
||||
let audiences: string;
|
||||
let hhan: string;
|
||||
const siteRequests: string[] = [];
|
||||
const metaRequests: string[] = [];
|
||||
|
||||
beforeAll(async () => {
|
||||
tmdb = await createMockTmdb({
|
||||
@@ -78,6 +79,8 @@ describe.skipIf(!pg)("ID-3 识别升级链:详情页 ID", () => {
|
||||
fx("tv", 290001, "Sample Series", "示例剧集", "2026-08-01", "tt45950001"),
|
||||
// 豆瓣路径:只凭豆瓣条目(标题 + 年份)匹配
|
||||
fx("tv", 990021, "Lanterns of Qing", "青灯记", "2025-04-01"),
|
||||
// bgm 路径:罗马音标题搜不到,bgm 条目中文名能搜到
|
||||
fx("tv", 990031, "Frieren: Beyond Journey's End", "葬送的芙莉莲", "2023-09-29"),
|
||||
],
|
||||
});
|
||||
h = await createHarness("id3", {
|
||||
@@ -94,6 +97,25 @@ describe.skipIf(!pg)("ID-3 识别升级链:详情页 ID", () => {
|
||||
if (u.host === "hhanclub.net" && u.pathname === "/viewfilelist.php") return html(HHANCLUB_FILELIST_FRAGMENT);
|
||||
throw new Error(`测试不允许的站点请求 ${u.href}`);
|
||||
},
|
||||
// bgm.tv:第二季条目(按站点给的 ID)与搜索(公开 BT 站兜底)
|
||||
metaFetch: async (input, init) => {
|
||||
const u = new URL(typeof input === "string" ? input : input instanceof URL ? input.href : input.url);
|
||||
metaRequests.push(`${init?.method ?? "GET"} ${u.host}${u.pathname}`);
|
||||
const json = (b: unknown) =>
|
||||
new Response(JSON.stringify(b), { headers: { "content-type": "application/json" } });
|
||||
if (u.host === "api.bgm.tv" && u.pathname === "/v0/subjects/515759")
|
||||
return json({
|
||||
id: 515759,
|
||||
name: "葬送のフリーレン 第2期",
|
||||
name_cn: "葬送的芙莉莲 第二季",
|
||||
date: "2026-01-16",
|
||||
});
|
||||
if (u.host === "api.bgm.tv" && u.pathname === "/v0/search/subjects")
|
||||
return json({
|
||||
data: [{ id: 400602, name: "葬送のフリーレン", name_cn: "葬送的芙莉莲", date: "2023-09-29" }],
|
||||
});
|
||||
return new Response("", { status: 404 });
|
||||
},
|
||||
});
|
||||
audiences = (await seedSite(h, { key: "audiences", baseUrl: "https://audiences.me/", cookie: "c_secure_uid=1" }))
|
||||
.site.id;
|
||||
@@ -304,7 +326,7 @@ describe.skipIf(!pg)("ID-3 识别升级链:详情页 ID", () => {
|
||||
escalation: { via: "douban", douban: { id: "36000099", title: "青灯记", source: "web_snippet" } },
|
||||
});
|
||||
|
||||
// 缓存里没有这个豆瓣条目:不请求豆瓣,只进池;有 ID 的资源不开「没识别出作品」
|
||||
// 缓存里没有这个豆瓣条目、在线也查不到(404):只进池;有 ID 的资源不开「没识别出作品」
|
||||
await sharedCache(h.ctx).delete(DOUBAN_SUBJECT_REGION, "36000098");
|
||||
const r2 = await release(hhan, "91006", "Unknown Drama S01E01 2025 1080p WEB-DL", {
|
||||
_detail: { doubanId: "36000098" },
|
||||
@@ -316,6 +338,47 @@ describe.skipIf(!pg)("ID-3 识别升级链:详情页 ID", () => {
|
||||
expect(siteRequests.some((x) => x.includes("douban"))).toBe(false);
|
||||
});
|
||||
|
||||
it("bgm 路径:AniBT 给的 bgm ID → 第二季条目去掉季名与年份 → TMDB;公开 BT 站没 ID 时按标题搜 bgm", async () => {
|
||||
const pub = await h.db
|
||||
.insertInto("site")
|
||||
.values({
|
||||
key: "anibt",
|
||||
name: "AniBT",
|
||||
kind: "public_bt",
|
||||
adapter: "api",
|
||||
base_url: "https://anibt.net/",
|
||||
} as never)
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
const r = await release(pub.id, "rel_A1", "[Nix-Raws] Sousou no Frieren S2 - 05 [1080p]", {
|
||||
_site: { bangumiId: "515759" },
|
||||
});
|
||||
const res = await resolveRelease(h.rt, { sourceReleaseId: r.id, trigger: "manual" });
|
||||
expect(res.outcome).toBe("auto");
|
||||
expect(await tmdbIdOf(res.mediaEntityId!)).toBe("990031");
|
||||
expect((await row(r.id)).resolution_evidence).toMatchObject({
|
||||
escalation: { via: "bangumi", bangumi: { id: "515759", search: false } },
|
||||
});
|
||||
// 条目进缓存:再识别同 ID 不再请求
|
||||
const n = metaRequests.length;
|
||||
const r2 = await release(pub.id, "rel_A2", "[Nix-Raws] Sousou no Frieren S2 - 06 [1080p]", {
|
||||
_site: { bangumiId: "515759" },
|
||||
});
|
||||
await resolveRelease(h.rt, { sourceReleaseId: r2.id, trigger: "manual" });
|
||||
expect(metaRequests.length).toBe(n);
|
||||
|
||||
const r3 = await release(pub.id, "rel_A3", "[SubsPlease] Sousou no Frieren - 03 (1080p)");
|
||||
const res3 = await resolveRelease(h.rt, { sourceReleaseId: r3.id, trigger: "manual" });
|
||||
expect(res3.outcome).toBe("auto");
|
||||
expect(await tmdbIdOf(res3.mediaEntityId!)).toBe("990031");
|
||||
expect(metaRequests).toContain("POST api.bgm.tv/v0/search/subjects");
|
||||
// PT 站的资源不按标题搜 bgm
|
||||
const m = metaRequests.length;
|
||||
const r4 = await release(hhan, "91099", "Sousou no Frieren S01E03 1080p WEB-DL");
|
||||
await resolveRelease(h.rt, { sourceReleaseId: r4.id, trigger: "manual" });
|
||||
expect(metaRequests.length).toBe(m);
|
||||
});
|
||||
|
||||
it("升级链没认出(豆瓣召回无匹配)时保留原有歧义:候选不丢,照常开有歧义的事项", async () => {
|
||||
await saveDoubanSubject(h.ctx, {
|
||||
id: "36000097",
|
||||
|
||||
Reference in new issue
Block a user