/* build-create-prompts.mjs — 构造 6 个视频的 AI 创作任务 prompt(与前端 AI创作 按钮完全一致:人设卡+视频选题块+创作指令),并提交 /api/run */ const { DatabaseSync } = require('node:sqlite'); const path = require('path'); const DB = path.join(__dirname, 'mcn-plugin.db'); const API = 'http://localhost:8900/api/run'; const ACCOUNT_NAME = '俊希'; const ACCOUNT_ID = 3; const VIDEO_IDS = [309, 313, 315, 316, 318, 320]; const db = new DatabaseSync(DB, { readOnly: true }); // —— 与前端一致的函数(data-pages.js)—— const stripTitleQuotes = (s) => String(s || '').trim().replace(/^["“”'‘’「『]+|["“”'‘’」』]+$/g, ''); const cleanTitle = (t) => { const s = String(t || '').trim(); const idx = s.indexOf('#'); if (idx <= 0) return stripTitleQuotes(s); return stripTitleQuotes(s.slice(0, idx)) + s.slice(idx); }; const personaForCreate = (text) => { const s = String(text || '').trim(); const idx = s.indexOf('## 十一、'); return idx >= 0 ? s.slice(0, idx).trim() : s; }; const extractVideoTopic = (content) => { const s = String(content || ''); const i = s.indexOf('# 选题'); if (i < 0) return ''; const rest = s.slice(i + '# 选题'.length); const j = rest.search(/\n# /); return (j >= 0 ? rest.slice(0, j) : rest).trim(); }; // —— 读俊希 persona —— const p = db.prepare('SELECT content_json FROM account_persona WHERE account_id=? ORDER BY id DESC LIMIT 1').get(ACCOUNT_ID); const personaText = personaForCreate(p ? p.content_json : ''); console.log('persona len:', personaText.length); const tasks = []; for (const vid of VIDEO_IDS) { const v = db.prepare('SELECT id, aweme_id, video_title, video_url FROM account_videos WHERE id=?').get(vid); if (!v) { console.error('video not found', vid); continue; } const s = db.prepare('SELECT content_json FROM account_video_source WHERE aweme_id=?').get(v.aweme_id); const content = String(s ? s.content_json : ''); const title = cleanTitle(v.video_title); // 前端 buildTopicBlock 逻辑 const tc = extractVideoTopic(content); let block, mode, createTail; if (tc) { block = `\n【视频选题】\n标题:《${title}》\n# 选题\n${tc}\n`; mode = 'topic'; createTail = '请结合账号设定与视频选题,创作一个全新的故事脚本(新故事、新情节,不要复刻原视频),输出完整故事脚本。'; } else { const body = content.trim(); if (!body) { block = `\n【视频选题】\n标题:《${title}》\n`; mode = 'none'; createTail = '请结合账号设定与视频选题,创作一个全新的故事脚本(新故事、新情节,不要复刻原视频),输出完整故事脚本。'; } else { block = `\n【视频解析内容(供提炼选题)】\n标题:《${title}》\n${body}\n`; mode = 'content'; createTail = '请先通读以上【视频解析内容】,提炼出该视频的选题(赛道/受众/视频主题/开场钩子/中间留人/结尾落点等),再结合账号设定与提炼出的选题,创作一个全新的故事脚本(新故事、新情节,不要复刻原视频),输出完整故事脚本。'; } } const personaBlock = personaText ? `\n【账号设定(人设卡)】\n${personaText}\n` : ''; const noPersonaNote = personaText ? '' : `\n该账号暂无账号设定,请先为达人「${ACCOUNT_NAME}」提取账号设定(生成人设卡),再基于账号设定与选题创作。`; const prompt = `请调用 Skill 工具加载「短视频脚本创作」技能(技能根目录 = 用户技能目录 ~/.workbuddy/skills/短视频脚本创作/,先读取其 SKILL.md 及 references 规范;若 Skill 工具无法加载,请直接读取该目录下的 SKILL.md 文件)。遵循 S1-S11 完整创作流程,最终输出 S9 标准脚本格式(按场次+对白组织,画面标注【景别|环境|光线|场景|场景元素】),禁止输出分镜头表格。 创作时必须逐条读取并严格执行 SKILL.md 的「开场钩子铁律」与【账号设定】的「内容规则」(如果…则…)——二者为硬约束,不得自由发挥、不得跳过,具体标准一律以 SKILL.md 与内容规则原文为准。 请为达人「${ACCOUNT_NAME}」创作一条全新的短视频故事脚本。${personaBlock}${noPersonaNote}${block}${createTail} 【任务完成后必须执行】 将完整脚本保存到工作台数据库 mcn-plugin.db 的 rewrite_log 表(数据库路径:D:\\AgentSkill\\mcn-video-script\\project\\短视频脚本创作\\V1.0\\mcn-work-shop\\mcn-plugin.db),字段: - video_id=${v.id}(整数) - aweme_id=${v.aweme_id} - account_id=${ACCOUNT_ID} - title=脚本标题 - script_text=完整脚本全文 - status='done' (用 node:sqlite 或 sqlite3 写入;若该表不存在则创建后再写) 完成后回复:脚本标题 + 是否已保存到数据库。`; tasks.push({ videoId: v.id, mode, title, prompt }); console.log(`TASK v${v.id} | mode=${mode} | promptLen=${prompt.length} | ${title.slice(0, 30)}`); } db.close(); // —— 提交任务(逐个串行,间隔 3s)—— const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); (async () => { const results = []; for (const t of tasks) { try { const resp = await fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: t.prompt, name: '创作指令', skills: ['短视频脚本创作'] }), }); const d = await resp.json(); results.push({ videoId: t.videoId, id: d.id, ok: d.ok, error: d.error }); console.log(`SUBMIT v${t.videoId} →`, d.ok ? ('id=' + d.id) : ('ERR ' + d.error)); } catch (e) { results.push({ videoId: t.videoId, error: e.message }); console.log(`SUBMIT v${t.videoId} → EXCEPTION ${e.message}`); } await sleep(3000); } console.log('\n=== RESULTS ==='); console.log(JSON.stringify(results, null, 1)); })();