feat(identify-words): 识别词——全局识别词(MP 语法)+ 作品识别词(季集换算),规则页「识别词」页签
- 新表 identify_words(0094):全局一块 + 每部作品一块原文,带版本与审计;episode_mapping_rule 转成文本行后删表
- 全局识别词移植 MP WordsMatcher(屏蔽 / 替换 / 集数偏移 / 替换+偏移 / {[tmdbid=…]}),所有按标题 / 文件名识别的地方解析前套用,命中的行记在解析结果上;保存后后台按改动行重新识别
- 作品识别词:S02 >> S01 EP+24 / S01E13 >> S00E01 / S01 @组 >> 忽略,解析器 + 打印器 + 编译;发布组繁简视为同一个
- 去掉规则自动停用:算出来对不上季表只待确认,要停用就注释掉那一行
- 集数待确认的采用 / 忽略与智能体 apply_rule 改为往作品识别词追加一行(带来历注释)
- 编辑器可视化 / 文本两种模式改同一份原文,保存前试算;作品页「集数映射」用同一个编辑器
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
12f8fc59c3
commit
63e94b308e
90 files changed
+6366
-2131
No files matched your search
@@ -3,60 +3,37 @@ import {
|
||||
Api,
|
||||
EpisodeMappingPreview,
|
||||
EpisodeMappingPreviewInput,
|
||||
EpisodeMappingRuleCreate,
|
||||
EpisodeMappingRuleUpdate,
|
||||
EpisodeResolutionView,
|
||||
listEndpoints,
|
||||
} from "../src/index.ts";
|
||||
|
||||
// 集数映射接口(批次 4)的契约:端点登记、写入与预览的入参、解析结果结构。
|
||||
// 集数映射与识别词接口的契约:端点登记、预览入参、解析结果结构。
|
||||
|
||||
describe("集数映射契约", () => {
|
||||
it("端点登记在 Api 注册表:读用 read,写用 organize.execute", () => {
|
||||
it("端点登记在 Api 注册表:读和试算用 read,作品识别词写 organize.execute,全局写 admin", () => {
|
||||
const keys = listEndpoints()
|
||||
.filter((e) => e.path.includes("episode-mapping"))
|
||||
.filter((e) => e.path.includes("episode-mapping") || e.path.includes("identify-words"))
|
||||
.map((e) => `${e.method.toUpperCase()} ${e.path} ${e.scope}`)
|
||||
.sort();
|
||||
expect(keys).toEqual([
|
||||
"DELETE /episode-mapping-rules/{id} organize.execute",
|
||||
"GET /media-entities/{id}/episode-mapping-rules read",
|
||||
"PATCH /episode-mapping-rules/{id} organize.execute",
|
||||
"POST /media-entities/{id}/episode-mapping-rules organize.execute",
|
||||
"GET /identify-words/entities read",
|
||||
"GET /identify-words/global read",
|
||||
"GET /media-entities/{id}/identify-words read",
|
||||
"POST /identify-words/global/dry-run read",
|
||||
"POST /identify-words/try read",
|
||||
"POST /media-entities/{id}/episode-mapping/preview read",
|
||||
"POST /media-entities/{id}/identify-words/dry-run read",
|
||||
"PUT /identify-words/global admin",
|
||||
"PUT /media-entities/{id}/identify-words organize.execute",
|
||||
]);
|
||||
expect(Api.episodeMapping.preview.response).toBe(EpisodeMappingPreview);
|
||||
});
|
||||
|
||||
it("新建:三种规则都接受;季号不能为负,发布组去首尾空白", () => {
|
||||
expect(EpisodeMappingRuleCreate.parse({ kind: "expression", expression: "EP-12", targetSeason: 2 })).toEqual({
|
||||
kind: "expression",
|
||||
expression: "EP-12",
|
||||
targetSeason: 2,
|
||||
});
|
||||
expect(
|
||||
EpisodeMappingRuleCreate.safeParse({
|
||||
season: 1,
|
||||
releaseGroup: "ANi",
|
||||
kind: "table",
|
||||
tableEntries: [{ from: { start: 27, end: 28 }, to: { season: 2, episode: 2 } }],
|
||||
}).success,
|
||||
).toBe(true);
|
||||
expect(
|
||||
EpisodeMappingRuleCreate.parse({ kind: "special", special: "ignore", releaseGroup: " ANi " }).releaseGroup,
|
||||
).toBe("ANi");
|
||||
expect(EpisodeMappingRuleCreate.safeParse({ kind: "special", special: "skip" }).success).toBe(false);
|
||||
expect(EpisodeMappingRuleCreate.safeParse({ kind: "expression", season: -1 }).success).toBe(false);
|
||||
expect(EpisodeMappingRuleCreate.safeParse({ kind: "expression", expression: "E".repeat(65) }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("修改:全部可选,可只改状态", () => {
|
||||
expect(EpisodeMappingRuleUpdate.parse({ status: "disabled" })).toEqual({ status: "disabled" });
|
||||
expect(EpisodeMappingRuleUpdate.safeParse({ status: "deleted" }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("预览入参:草稿可空,条数上限 300", () => {
|
||||
it("预览入参:草稿是整块识别词原文,条数上限 300", () => {
|
||||
expect(EpisodeMappingPreviewInput.parse({})).toEqual({});
|
||||
expect(EpisodeMappingPreviewInput.safeParse({ season: 2, releaseGroup: "ANi", draft: null }).success).toBe(true);
|
||||
expect(
|
||||
EpisodeMappingPreviewInput.safeParse({ season: 2, releaseGroup: "ANi", words: "S02 >> S01 EP+24" }).success,
|
||||
).toBe(true);
|
||||
expect(EpisodeMappingPreviewInput.safeParse({ limit: 301 }).success).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user