feat(发布计划): 标题行两种写法都认 + 行首 # 不再无条件当注释
一、标题行解析(core/video_plan.parse_title_line) - 原来只认「标题内容_手机号_日期_编号」(右锚定)。现在**两种都认**: ① 标题内容_手机号_日期_编号 (推荐,右锚定 —— 标题里带下划线也不会错配) ② 手机号_日期_编号_标题 (左锚定,跟视频文件名同序 = "文件名去掉扩展名 + 标题") 两种里都**先试带编号的**,保证同一行永远只有一种解释;编号都可省略。 - 新拒收一条:`手机号_日期_1`(只有编号、没标题)**直接拒** —— 不能把它当标题"1"(静默生成一条标题是"1"的文案,比拒收危险得多); 标题真是纯数字的用写法①。 二、`#` 开头的行(parse_titles_text) - 原来是"以 `#` 开头就整行忽略" → **`#中秋快乐_...` 这种正常标题会被静默丢掉**。 - 改成**先按标题行解析,解析得出就当标题;解析不出且以 `#` 开头才算注释**。 `# 这是注释` 照样忽略,`#话题` 开头的标题照收(`#` 保留在标题里)。 三、其它 - 文案同步:上传标题面板/帮助文案、doc/API.md 的 upload_titles 语义(两种写法、`#` 规则、拒收条件) - 测试:新增 8 条(两种写法 × 带/不带编号 × 标题含下划线与 #话题、只有编号要拒收、整段注释与报错)
This commit is contained in:
+39
-8
@@ -162,32 +162,56 @@ def parse_video_name(filename):
|
||||
|
||||
|
||||
def parse_title_line(line):
|
||||
"""标题行 `标题内容_手机号_日期_编号` → (meta, error)。标题里可以带下划线。
|
||||
"""标题行 → (meta, error)。**两种写法都认**,标题里都可以带下划线。
|
||||
|
||||
右锚定 + 逐级回退(**必须先试带编号的**,否则带编号的行会被当成
|
||||
"标题里含日期"而错配):
|
||||
A) …_手机号_日期_编号 B) …_手机号_日期
|
||||
① 标题在前(推荐,右锚定):
|
||||
A) 标题内容_手机号_日期_编号 B) 标题内容_手机号_日期
|
||||
② 手机号在前(左锚定,跟视频文件名同序 —— "文件名去掉扩展名 + 标题"):
|
||||
C) 手机号_日期_编号_标题 D) 手机号_日期_标题
|
||||
|
||||
为什么**先右后左**:右锚定是主格式(标题里带下划线也不会错配)。
|
||||
左锚定的四种里先试带编号的 C(`手机号_日期_1_标题` —— 那个 `1` 当编号,
|
||||
不当标题的开头),保证同一行永远只有一种解释。
|
||||
|
||||
什么时候两种都不会误判:右锚定要求"倒数第 3/2 段是手机号",左锚定要求
|
||||
"第 1 段是手机号、第 2 段是日期" —— 一行同时满足两边时(例如 `手机号_日期_1`)
|
||||
右锚定先命中,且标题为空会被下面的校验拒掉,不会静默发出一条错文案。
|
||||
"""
|
||||
t = (line or "").strip()
|
||||
if not t:
|
||||
return None, "空行"
|
||||
parts = t.split("_")
|
||||
phone = date = seq = None
|
||||
title_parts = None
|
||||
# ① 右锚定:先试带编号的,再试不带编号的
|
||||
if len(parts) >= 4 and _SEQ_RE.match(parts[-1]) \
|
||||
and _DATE8_RE.match(parts[-2]) and _PHONE_RE.match(parts[-3]):
|
||||
seq = int(parts[-1])
|
||||
phone, date8, title_parts = parts[-3], parts[-2], parts[:-3]
|
||||
elif len(parts) >= 3 and _DATE8_RE.match(parts[-1]) and _PHONE_RE.match(parts[-2]):
|
||||
phone, date8, title_parts = parts[-2], parts[-1], parts[:-2]
|
||||
# ② 左锚定(手机号在前,跟视频文件名同序):同样先试带编号的
|
||||
elif len(parts) >= 4 and _PHONE_RE.match(parts[0]) and _DATE8_RE.match(parts[1]) \
|
||||
and _SEQ_RE.match(parts[2]):
|
||||
phone, date8, seq = parts[0], parts[1], int(parts[2])
|
||||
title_parts = parts[3:]
|
||||
elif len(parts) >= 3 and _PHONE_RE.match(parts[0]) and _DATE8_RE.match(parts[1]):
|
||||
# `手机号_日期_1` 这种"只有编号、没有标题"要**拒掉**,不能被当成标题"1"
|
||||
# (静默生成一条标题是"1"的文案,比拒收危险得多;标题真是纯数字就用"标题在前"的写法)
|
||||
if len(parts) == 3 and _SEQ_RE.match(parts[2]):
|
||||
return None, ("只有手机号/日期/编号、没有标题文字 ——"
|
||||
"标题是纯数字的话请用「标题_手机号_日期」写法")
|
||||
phone, date8, title_parts = parts[0], parts[1], parts[2:]
|
||||
else:
|
||||
return None, "格式不对,应为 标题内容_手机号_日期_编号(编号可省略)"
|
||||
return None, ("格式不对,应为「标题内容_手机号_日期_编号」"
|
||||
"或「手机号_日期_编号_标题」(编号都可省略)")
|
||||
d = parse_date8(date8)
|
||||
if not d:
|
||||
return None, f"日期 {date8} 不是合法日期(应为 20260912 这种 8 位)"
|
||||
date = d
|
||||
title = "_".join(title_parts).strip()
|
||||
if not title:
|
||||
return None, "缺标题内容(行首就是手机号)"
|
||||
return None, "缺标题内容(只有手机号/日期/编号,没有标题文字)"
|
||||
if len(title) > MAX_TITLE_LEN:
|
||||
return None, f"标题太长({len(title)} 字 > {MAX_TITLE_LEN} 字上限),请改短或拆成两条"
|
||||
return {"title": title, "phone": phone, "date": date,
|
||||
@@ -195,15 +219,22 @@ def parse_title_line(line):
|
||||
|
||||
|
||||
def parse_titles_text(text):
|
||||
"""标题 txt 全文 → (rows, errors)。`#` 开头的行与空行自动跳过(不算错)。"""
|
||||
"""标题 txt 全文 → (rows, errors)。空行跳过;`#` 开头**只有认不出标题行时**才算注释。
|
||||
|
||||
⚠ 顺序是"先当标题解析、再当注释":`#中秋快乐_19286456013_20260929` 是一条**正常标题**
|
||||
(`#话题` 本来就长这样),过去的写法会把整行当注释**静默丢掉**。现在只有像
|
||||
`# 这是注释` 这种解析不出手机号/日期的行才按注释忽略。
|
||||
"""
|
||||
rows, errors = [], []
|
||||
lines = (text or "").replace("\r\n", "\n").replace("\r", "\n").split("\n")
|
||||
for i, raw in enumerate(lines, start=1):
|
||||
s = raw.strip()
|
||||
if not s or s.startswith("#"):
|
||||
if not s:
|
||||
continue
|
||||
meta, err = parse_title_line(s)
|
||||
if err:
|
||||
if s.startswith("#"): # 认不出 + 以 # 开头 → 当注释,静默跳过
|
||||
continue
|
||||
errors.append({"line": i, "reason": err, "raw": s[:60]})
|
||||
continue
|
||||
meta["_line"] = i
|
||||
|
||||
Reference in New Issue
Block a user