- settings.naming {movie, tv, variety, lockFileNameForBound}, default = MP templates;
library section gains #naming anchor
- naming API schemas and Api.naming endpoints (variables, preview, templates, overrides)
- 0032: library_root.naming, media_entity.naming_override, naming_template_version
(version 1 = upgraded v1 templates, 2 = MP default, fingerprint-deduplicated)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
70 lines
3.6 KiB
TypeScript
70 lines
3.6 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import type { Kysely } from "kysely";
|
|
import { dropTables, entityTable, execDdl } from "../src/migration-helpers.ts";
|
|
|
|
// W9-C 整理命名模板(docs/design/naming-template.md §6、§9):
|
|
// - library_root.naming / media_entity.naming_override:按类型(movie/tv/variety)覆盖模板,null = 不覆盖。
|
|
// 生效顺序 作品 > 库根 > 全局(settings.naming)。
|
|
// - naming_template_version:生效模板集的版本登记(指纹去重),path_binding.template_version 引用 version;
|
|
// "已绑定作品固定文件名模板版本"按它取回当时的模板。1 = v1 旧模板(迁移后语法),2 = MP 默认。
|
|
// 模板串是写入时的快照,之后不随代码变化。
|
|
|
|
const LEGACY_V1 = {
|
|
movie:
|
|
"{{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if cut %} - {{cut}}{% endif %}{% if part %} - {{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}}",
|
|
tv: "{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %} - {{part}}{% endif %}{{fileExt}}",
|
|
variety:
|
|
"{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{% if season_episode %}{{title}} - {{season_episode}}{% if part %} - {{part}}{% endif %}{% else %}{{title}} - {{airDate | date('YYYY-MM-DD')}}{% if issue %} - 第{{issue}}期{% endif %}{% endif %}{{fileExt}}",
|
|
};
|
|
const MP_DEFAULT = {
|
|
movie:
|
|
"{{title}}{% if year %} ({{year}}){% endif %}/{{title}}{% if year %} ({{year}}){% endif %}{% if part %}-{{part}}{% endif %}{% if videoFormat %} - {{videoFormat}}{% endif %}{{fileExt}}",
|
|
tv: "{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {{season_episode}}{% if part %}-{{part}}{% endif %}{% if episode %} - 第 {{episode}} 集{% endif %}{{fileExt}}",
|
|
variety:
|
|
"{{title}}{% if year %} ({{year}}){% endif %}/Season {{season}}/{{title}} - {% if season_episode and episode %}{{season_episode}}{% else %}{{airDate | date('YYYY-MM-DD')}}{% endif %}{% if issue %} - 第 {{issue}} 期{% endif %}{{fileExt}}",
|
|
};
|
|
|
|
/** 与 @umt/domain namingFingerprint 同算法:sha256(JSON {movie,tv,variety}) */
|
|
const fingerprint = (t: { movie: string; tv: string; variety: string }) =>
|
|
createHash("sha256")
|
|
.update(JSON.stringify({ movie: t.movie, tv: t.tv, variety: t.variety }))
|
|
.digest("hex");
|
|
const lit = (s: string) => `'${s.replace(/'/g, "''")}'`;
|
|
const seed = (version: number, t: typeof MP_DEFAULT) =>
|
|
`insert into naming_template_version (version, fingerprint, templates) values (${version}, ${lit(fingerprint(t))}, ${lit(JSON.stringify(t))}::jsonb);`;
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 迁移不依赖应用表类型
|
|
export async function up(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`
|
|
alter table library_root add column naming jsonb check (naming is null or jsonb_typeof(naming) = 'object');
|
|
alter table media_entity add column naming_override jsonb check (naming_override is null or jsonb_typeof(naming_override) = 'object');
|
|
|
|
${entityTable(
|
|
"naming_template_version",
|
|
`
|
|
version integer not null unique check (version >= 1),
|
|
fingerprint text not null unique,
|
|
templates jsonb not null check (jsonb_typeof(templates) = 'object')
|
|
`,
|
|
)}
|
|
create sequence naming_template_version_seq start with 3 owned by naming_template_version.version;
|
|
${seed(1, LEGACY_V1)}
|
|
${seed(2, MP_DEFAULT)}
|
|
`,
|
|
);
|
|
}
|
|
|
|
// biome-ignore lint/suspicious/noExplicitAny: 同上
|
|
export async function down(db: Kysely<any>): Promise<void> {
|
|
await execDdl(
|
|
db,
|
|
`
|
|
${dropTables(["naming_template_version"])}
|
|
alter table media_entity drop column naming_override;
|
|
alter table library_root drop column naming;
|
|
`,
|
|
);
|
|
}
|