copy(domain): 命名模板、规则判定、收录版本评估文案改全角标点并去掉内部用语
阶梯 pri / 候选 / 资产交付特征等改成界面用词;同步断言。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
12a8caa463
commit
0ba947dce3
14 files changed
+86
-80
No files matched your search
@@ -58,16 +58,16 @@ export interface CompileOptions {
|
||||
// ── 过滤器 ──
|
||||
|
||||
export const NAMING_FILTERS = {
|
||||
default: { args: [1, 2], doc: "值为空时用参数代替:default('未知')" },
|
||||
pad: { args: [1, 1], doc: "数字左补零:pad(2) → 03;区间 1-3 → 01-03" },
|
||||
default: { args: [1, 2], doc: "值为空时用参数代替:default('未知')" },
|
||||
pad: { args: [1, 1], doc: "数字左补零:pad(2) → 03;区间 1-3 → 01-03" },
|
||||
pad2: { args: [0, 0], doc: "等同 pad(2)" },
|
||||
upper: { args: [0, 0], doc: "转大写" },
|
||||
lower: { args: [0, 0], doc: "转小写" },
|
||||
trim: { args: [0, 0], doc: "去首尾空白" },
|
||||
replace: { args: [2, 2], doc: "替换:replace('.', ' ')" },
|
||||
truncate: { args: [1, 1], doc: "按字符截断:truncate(40)" },
|
||||
date: { args: [1, 1], doc: "YYYYMMDD / YYYY-MM-DD 日期格式化:date('YYYY-MM-DD'|'YYYY.MM.DD'|'YYYYMMDD')" },
|
||||
join: { args: [0, 1], doc: "列表值按分隔符连接(默认空格);标量原样" },
|
||||
replace: { args: [2, 2], doc: "替换:replace('.', ' ')" },
|
||||
truncate: { args: [1, 1], doc: "按字符截断:truncate(40)" },
|
||||
date: { args: [1, 1], doc: "YYYYMMDD / YYYY-MM-DD 日期格式化:date('YYYY-MM-DD'|'YYYY.MM.DD'|'YYYYMMDD')" },
|
||||
join: { args: [0, 1], doc: "列表值按分隔符连接(默认空格);标量原样" },
|
||||
} as const;
|
||||
export type FilterName = keyof typeof NAMING_FILTERS;
|
||||
const DATE_FORMATS = ["YYYY-MM-DD", "YYYY.MM.DD", "YYYYMMDD"];
|
||||
@@ -234,10 +234,10 @@ function lex(body: string, base: number): Tok[] {
|
||||
}
|
||||
if (c === ".") throw new Fail("attribute_access", "不支持属性访问(a.b)", pos);
|
||||
if (c === "[") throw new Fail("subscript", "不支持下标访问(a[0])", pos);
|
||||
if (c === "~") throw new Fail("concat", "不支持 ~ 拼接,直接把两个 {{ }} 写在一起", pos);
|
||||
if (c === "%") throw new Fail("arithmetic", "不支持 % 格式化/运算;补零用 {{ season | pad(2) }}", pos);
|
||||
if (c === "~") throw new Fail("concat", "不支持 ~ 拼接,直接把两个 {{ }} 写在一起", pos);
|
||||
if (c === "%") throw new Fail("arithmetic", "不支持 % 格式化/运算;补零用 {{ season | pad(2) }}", pos);
|
||||
if ("+-*/".includes(c)) throw new Fail("arithmetic", "不支持算术运算", pos);
|
||||
if (c === "=") throw new Fail("assignment", "不支持赋值;比较用 ==", pos);
|
||||
if (c === "=") throw new Fail("assignment", "不支持赋值;比较用 ==", pos);
|
||||
throw new Fail("bad_char", `无法识别的字符 ${JSON.stringify(c)}`, pos);
|
||||
}
|
||||
toks.push({ k: "end", pos: base + body.length });
|
||||
@@ -303,7 +303,7 @@ class ExprParser {
|
||||
const e = this.parseOr();
|
||||
const t = this.peek();
|
||||
if (t.k !== "end") {
|
||||
if (t.k === "id" && t.v === "if") throw new Fail("inline_if", "不支持行内 if 表达式,改用 {% if %}", t.pos);
|
||||
if (t.k === "id" && t.v === "if") throw new Fail("inline_if", "不支持行内 if 表达式,改用 {% if %}", t.pos);
|
||||
if (t.k === "id" && own(KEYWORDS_UNSUPPORTED, t.v))
|
||||
throw new Fail("unsupported", own(KEYWORDS_UNSUPPORTED, t.v)!, t.pos);
|
||||
throw new Fail("syntax", "表达式多余内容", t.pos);
|
||||
@@ -354,7 +354,7 @@ class ExprParser {
|
||||
const hint = didYouMean(n.v, Object.keys(NAMING_FILTERS));
|
||||
throw new Fail(
|
||||
"unknown_filter",
|
||||
`不支持的过滤器 ${n.v}${hint ? `,是否指 ${hint}` : ""}(可用:${Object.keys(NAMING_FILTERS).join("/")})`,
|
||||
`不支持的过滤器 ${n.v}${hint ? `,是否指 ${hint}` : ""}(可用:${Object.keys(NAMING_FILTERS).join("/")})`,
|
||||
n.pos,
|
||||
);
|
||||
}
|
||||
@@ -406,7 +406,7 @@ class ExprParser {
|
||||
const known = this.opts.knownVars;
|
||||
if (known && !known.includes(t.v)) {
|
||||
const hint = didYouMean(t.v, known);
|
||||
throw new Fail("unknown_variable", `未知变量 ${t.v}${hint ? `,是否指 ${hint}` : ""}`, t.pos);
|
||||
throw new Fail("unknown_variable", `未知变量 ${t.v}${hint ? `,是否指 ${hint}` : ""}`, t.pos);
|
||||
}
|
||||
if (!this.vars.includes(t.v)) this.vars.push(t.v);
|
||||
return { t: "var", name: t.v, pos: t.pos };
|
||||
@@ -495,7 +495,7 @@ function compileInner(source: string, opts: CompileOptions): CompiledNaming {
|
||||
const f = stack[stack.length - 1];
|
||||
if (!f) throw new Fail("stray_else", "else 没有对应的 if", c.pos);
|
||||
if (f.sawElse) throw new Fail("syntax", "重复的 else", c.pos);
|
||||
if (rest.trim()) throw new Fail("syntax", "else 后不能带条件,用 elif", c.pos);
|
||||
if (rest.trim()) throw new Fail("syntax", "else 后不能带条件,用 elif", c.pos);
|
||||
f.sawElse = true;
|
||||
const body: Node[] = [];
|
||||
f.node.else = body;
|
||||
|
||||
@@ -129,7 +129,7 @@ export function renderNamingPath(
|
||||
return fail([
|
||||
at(
|
||||
"no_work_root",
|
||||
"识别不出作品目录:文件名之前至少要有一层目录包含 title / title_year / en_title / original_title / name,且不含季变量",
|
||||
"识别不出作品目录:文件名之前至少要有一层目录包含 title / title_year / en_title / original_title / name,且不含季变量",
|
||||
),
|
||||
]);
|
||||
const errors: NamingIssue[] = [];
|
||||
@@ -148,7 +148,7 @@ export function renderNamingPath(
|
||||
} else {
|
||||
text = cleanSegment(r.text, SEGMENT_MAX_BYTES);
|
||||
if (!text && role === "root")
|
||||
errors.push(at("empty_segment", `作品目录为空(${r.vars.join("、") || "无变量"} 没有值)`));
|
||||
errors.push(at("empty_segment", `作品目录为空(${r.vars.join("、") || "无变量"} 没有值)`));
|
||||
if (!text && role === "season") errors.push(at("empty_segment", "季目录为空"));
|
||||
}
|
||||
if (text || role === "root" || role === "file" || role === "season") segments.push({ text, role, vars: r.vars });
|
||||
@@ -179,7 +179,7 @@ export function validateNamingTemplate(source: string, kind: NamingKind): Naming
|
||||
const lines = source.split("\n");
|
||||
errors.push({
|
||||
code: "missing_file_ext",
|
||||
message: "文件名缺少 {{fileExt}}:模板必须以 {{fileExt}} 结尾",
|
||||
message: "文件名缺少 {{fileExt}}:模板必须以 {{fileExt}} 结尾",
|
||||
line: lines.length,
|
||||
col: (lines[lines.length - 1]?.length ?? 0) + 1,
|
||||
});
|
||||
@@ -190,7 +190,7 @@ export function validateNamingTemplate(source: string, kind: NamingKind): Naming
|
||||
if (kind === "tv") for (const v of ["episode", "season_episode"]) always.add(v);
|
||||
const warnings = unguardedOutputs(r.compiled)
|
||||
.filter((v) => !always.has(v))
|
||||
.map((v) => `${v} 可能为空,建议写成 {% if ${v} %}…{{${v}}}…{% endif %}`);
|
||||
.map((v) => `${v} 可能为空,建议写成 {% if ${v} %}…{{${v}}}…{% endif %}`);
|
||||
return {
|
||||
ok: errors.length === 0,
|
||||
errors,
|
||||
|
||||
@@ -19,39 +19,46 @@ const ALL: readonly NamingKind[] = ["movie", "tv", "variety"];
|
||||
const SERIES: readonly NamingKind[] = ["tv", "variety"];
|
||||
|
||||
export const NAMING_VARIABLES: readonly NamingVariableDef[] = [
|
||||
{ name: "title", group: "title", label: "作品标题(本地作品名)", example: "绝命毒师", ours: false, always: ALL },
|
||||
{ name: "title", group: "title", label: "作品标题(本地作品名)", example: "绝命毒师", ours: false, always: ALL },
|
||||
{ name: "en_title", group: "title", label: "英文标题", example: "Breaking Bad", ours: false },
|
||||
{ name: "original_title", group: "title", label: "原语种标题", example: "Breaking Bad", ours: false },
|
||||
{ name: "name", group: "title", label: "文件名识别出的标题", example: "Breaking Bad", ours: false },
|
||||
{ name: "year", group: "title", label: "年份(作品年份,缺失取文件名年份)", example: "2008", ours: false },
|
||||
{ name: "year", group: "title", label: "年份(作品年份,缺失取文件名年份)", example: "2008", ours: false },
|
||||
{
|
||||
name: "title_year",
|
||||
group: "title",
|
||||
label: "标题 (年份);无年份只有标题",
|
||||
label: "标题(年份);无年份只有标题",
|
||||
example: "绝命毒师 (2008)",
|
||||
ours: false,
|
||||
always: ALL,
|
||||
},
|
||||
{ name: "type", group: "title", label: "类型:电影 / 电视剧 / 综艺", example: "电视剧", ours: false, always: ALL },
|
||||
{ name: "type", group: "title", label: "类型:电影 / 电视剧 / 综艺", example: "电视剧", ours: false, always: ALL },
|
||||
{
|
||||
name: "category",
|
||||
group: "title",
|
||||
label: "二级分类(没有则为空,所在目录段整段省略)",
|
||||
label: "二级分类(没有则为空,所在目录段整段省略)",
|
||||
example: "欧美剧",
|
||||
ours: false,
|
||||
},
|
||||
{ name: "season", group: "episode", label: "季号,不补零;剧集缺季号为 1", example: "1", ours: false, always: SERIES },
|
||||
{ name: "season_fmt", group: "episode", label: "S01;多季 S01-S02", example: "S01", ours: false, always: SERIES },
|
||||
{
|
||||
name: "season",
|
||||
group: "episode",
|
||||
label: "季号,不补零;剧集缺季号为 1",
|
||||
example: "1",
|
||||
ours: false,
|
||||
always: SERIES,
|
||||
},
|
||||
{ name: "season_fmt", group: "episode", label: "S01;多季 S01-S02", example: "S01", ours: false, always: SERIES },
|
||||
{ name: "season_year", group: "episode", label: "该季首播年份", example: "2008", ours: false },
|
||||
{ name: "episode", group: "episode", label: "集号,不补零;多集 1-3", example: "1", ours: false },
|
||||
{ name: "season_episode", group: "episode", label: "S01E01;多集 S01E01-E03", example: "S01E01", ours: false },
|
||||
{ name: "total_episodes", group: "episode", label: "本季总集数,未知为 0", example: "7", ours: false, always: ALL },
|
||||
{ name: "episode", group: "episode", label: "集号,不补零;多集 1-3", example: "1", ours: false },
|
||||
{ name: "season_episode", group: "episode", label: "S01E01;多集 S01E01-E03", example: "S01E01", ours: false },
|
||||
{ name: "total_episodes", group: "episode", label: "本季总集数,未知为 0", example: "7", ours: false, always: ALL },
|
||||
{ name: "episode_title", group: "episode", label: "单集标题", example: "Pilot", ours: false },
|
||||
{ name: "episode_date", group: "episode", label: "单集播出日期", example: "2008-01-20", ours: false },
|
||||
{ name: "part", group: "episode", label: "分段(CD1、Part2)", example: "CD1", ours: false },
|
||||
{ name: "videoFormat", group: "tech", label: "分辨率", example: "2160p", ours: false },
|
||||
{ name: "resourceType", group: "tech", label: "来源", example: "WEB-DL", ours: false },
|
||||
{ name: "effect", group: "tech", label: "特效(HDR 格式,空格分隔)", example: "DV", ours: false },
|
||||
{ name: "effect", group: "tech", label: "特效(HDR 格式,空格分隔)", example: "DV", ours: false },
|
||||
{ name: "edition", group: "tech", label: "来源 + 特效", example: "WEB-DL DV", ours: false },
|
||||
{ name: "resource_term", group: "tech", label: "来源 + 特效 + 分辨率", example: "WEB-DL DV 2160p", ours: false },
|
||||
{ name: "releaseGroup", group: "tech", label: "发布组", example: "CMCT", ours: false },
|
||||
@@ -60,8 +67,8 @@ export const NAMING_VARIABLES: readonly NamingVariableDef[] = [
|
||||
{ name: "audioCodec", group: "tech", label: "音频编码", example: "DDP 5.1", ours: false },
|
||||
{ name: "webSource", group: "tech", label: "流媒体平台", example: "NF", ours: false },
|
||||
{ name: "fps", group: "tech", label: "帧率", example: "60", ours: false },
|
||||
{ name: "customization", group: "tech", label: "自定义占位符(暂恒为空)", example: "", ours: false },
|
||||
{ name: "fileExt", group: "tech", label: "扩展名,带点,小写", example: ".mkv", ours: false, always: ALL },
|
||||
{ name: "customization", group: "tech", label: "自定义占位符(暂恒为空)", example: "", ours: false },
|
||||
{ name: "fileExt", group: "tech", label: "扩展名,带点,小写", example: ".mkv", ours: false, always: ALL },
|
||||
{ name: "tmdbid", group: "id", label: "TMDB ID", example: "1396", ours: false },
|
||||
{ name: "imdbid", group: "id", label: "IMDb ID", example: "tt0903747", ours: false },
|
||||
{ name: "doubanid", group: "id", label: "豆瓣 ID", example: "1234567", ours: false },
|
||||
@@ -71,7 +78,7 @@ export const NAMING_VARIABLES: readonly NamingVariableDef[] = [
|
||||
{
|
||||
name: "airDate",
|
||||
group: "ours",
|
||||
label: "综艺播出日期 YYYYMMDD,配合 date() 格式化",
|
||||
label: "综艺播出日期 YYYYMMDD,配合 date() 格式化",
|
||||
example: "20260927",
|
||||
ours: true,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user