diff --git a/.workbuddy/memory/2026-09-01.md b/.workbuddy/memory/2026-09-01.md index 40595d5..a499647 100644 --- a/.workbuddy/memory/2026-09-01.md +++ b/.workbuddy/memory/2026-09-01.md @@ -42,3 +42,11 @@ - **验证闭环**:入库返回 xlsx_rows=137/video_analysis=6/video_source=6/videos=137/persona=1(去重);JOIN 俊希 analysis 6 条+source 6 条、null 脏数据 0;选题接口 query 已含 3 条真实视频解析(过生日 33916 字等),端到端 Dify 返回 3 选题全部基于解析内容基因(嘴甜夸赞/播音腔/家庭结构符号) - **技能文档同步**:mcn-dou-analysis SKILL.md 功能五「可选写库」补充工作台入库方式 + 注意事项第 6 条记录标题匹配红线(全角标点坑) - 服务 PID 27548→KKQ7LW(8900,修复后重启);git 待提交 + +## 09-01 10:16 视频列表表头记录 + 时长显示修复(用户发现:视频ID记录/无时长) +- **问题**:俊希视频列表出现「视频ID」假记录(aweme_id=视频ID/视频标题/视频时长);部分视频显示无时长 +- **根因 1(表头假记录)**:export_video_map.py 未过滤表头行——aweme_id='视频ID' 非空且 title 有值,被当数据行输出入库(id 652);xlsx 137 行=表头1+数据136 +- **根因 2(无时长)**:**数据其实全有时长**(库 136 条全有值、xlsx 无空无零),是前端 fmtDuration 把 duration 当秒数 `Number('01:31')`=NaN→显示 '-',而库存/接口返回的是 mm:ss 文本(13:48 等) +- **修复(治本)**:①export_video_map.py 增加 aweme_id 纯数字校验(`str(aweme_id).isdigit()`)过滤表头/非数据行;②data-pages.js fmtDuration 重写:兼容纯数字秒(648→10分48秒)与 mm:ss/hh:mm:ss 文本(01:31→1分31秒、13:48→13分48秒);③清理库内表头假记录(DELETE aweme_id='视频ID'/空/null,1条) +- **验证**:导出 136 条无非数字;重跑入库 xlsx_rows=136(表头不再计入)、videos_upserted=0;库 total=136/表头0/无时长0;/api/dsh/account-videos 返回 total=136、duration='13:48' 等 mm:ss → fmtDuration 正确显示 +- 待提交 git;页面待重开验证 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js index 438f385..5ff1827 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js @@ -18,7 +18,26 @@ const fmtTitleQ = (t) => { const rest = s.slice(idx).trim(); return '《' + title + '》' + (rest ? '\n' + rest : ''); }; -const fmtDuration = (v) => { const s = Number(v); if (!Number.isFinite(s) || s <= 0) return '-'; const m = Math.floor(s / 60); const sec = Math.round(s % 60); return m > 0 ? `${m}分${sec}秒` : `${sec}秒`; }; +// 时长格式化:兼容「数字秒」与「mm:ss / hh:mm:ss 文本」(xlsx 视频时长列为 mm:ss 文本,09-01 修复:旧实现只认秒数导致全部显示 '-') +const fmtDuration = (v) => { + if (v == null || String(v).trim() === '') return '-'; + const s = String(v).trim(); + if (/^\d+(\.\d+)?$/.test(s)) { + const n = Number(s); + if (!Number.isFinite(n) || n <= 0) return '-'; + const m = Math.floor(n / 60); const sec = Math.round(n % 60); + return m > 0 ? `${m}分${sec}秒` : `${sec}秒`; + } + const parts = s.split(':').map(Number); + if (parts.length < 2 || parts.some((x) => !Number.isFinite(x))) return '-'; + if (parts.length === 2) { + const [m, sec] = parts; + if (m === 0) return `${sec}秒`; + return `${m}分${String(sec).padStart(2, '0')}秒`; + } + const [h, m, sec] = parts; + return h > 0 ? `${h}时${String(m).padStart(2, '0')}分${String(sec).padStart(2, '0')}秒` : `${m}分${String(sec).padStart(2, '0')}秒`; +}; const fmtTime = (t) => { if (!t) return '-'; const d = new Date(t); if (Number.isNaN(d.getTime())) return String(t); const p = (n) => String(n).padStart(2, '0'); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`; }; function pagerHtml(total, page, pageSize, onChange) { diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/scripts/export_video_map.py b/project/短视频脚本创作/V1.0/mcn-work-shop/scripts/export_video_map.py index 9cf2cb1..b88f06c 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/scripts/export_video_map.py +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/scripts/export_video_map.py @@ -35,7 +35,8 @@ def main(): if not row or len(row) < 3: continue aweme_id, title = row[1], row[2] - if aweme_id is None or not title: + # 过滤表头/非数据行:aweme_id 必须是纯数字(抖音视频ID),表头行「视频ID」等非数字行直接跳过(09-01 修复:表头曾被当数据导入) + if aweme_id is None or not str(aweme_id).strip().isdigit() or not title: continue rec = { "aweme_id": str(aweme_id).strip(),