Files
mcn-short-video/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-verify-oneway2.mjs
T
maogeigei 96fce56fba feat(MCN工作台): 移植dsh导入账号功能 + 弹窗按钮/宽度优化 + 分镜技能归入subskill
- 账号列表页新增「导入账号」:两种方式(抖音账号信息/本地文件夹),复用 executeTask 任务链路提交 AI 任务
- 查看改写弹窗:展开源脚本按钮移入 AI脚本 标题右侧(间隔10px),弹窗最大宽度 1400→1600
- 短视频提示词生成 整体移入 短视频脚本创作/V1.0/subskill/mcn-video-prompt(与 mcn-dou-analysis 同构,138 文件 R100 保留历史)
- 工作台 dsh-data/server 若干修复 + 记忆更新
2026-08-28 18:54:01 +08:00

124 lines
7.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 验证「单向联动」v2:全部用 JS 设置 scrollTop 触发 scroll 事件(避免 mouseWheel SIGTERM)
const list = await fetch('http://localhost:9223/json').then((r) => r.json());
const page = list.find((t) => t.type === 'page' && t.url.includes('#/rewrites'));
if (!page) { console.log('ERR: no #/rewrites page'); process.exit(1); }
const ws = new WebSocket(page.webSocketDebuggerUrl);
let id = 0; const pending = new Map();
const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); });
ws.onmessage = (e) => { const m = JSON.parse(e.data); if (m.id && pending.has(m.id)) { const p = pending.get(m.id); pending.delete(m.id); m.error ? p.rej(new Error(m.error.message)) : p.res(m.result); } };
await new Promise((r) => (ws.onopen = r));
const ev = async (expr) => { const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true, awaitPromise: true }); if (r.exceptionDetails) return { __err: (r.exceptionDetails.exception?.description || r.exceptionDetails.text).slice(0, 300) }; return r.result.value; };
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
await send('Page.enable'); await send('Runtime.enable');
await send('Page.reload', { ignoreCache: true });
await sleep(3000);
await ev(`document.querySelectorAll('.modal-mask').forEach((m) => m.remove()); 'cleaned'`);
await ev(`(() => { const i = document.querySelector('#rwSearch'); if (i) { i.value = '快递'; i.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })); } return !!i; })()`);
await sleep(2000);
const clickRes = await ev(`(() => {
const btns = [...document.querySelectorAll('[data-detail]')];
if (!btns.length) return { err: 'no data-detail' };
btns[0].click();
return { clicked: btns[0].dataset.detail };
})()`);
console.log('打开弹窗:', JSON.stringify(clickRes));
await sleep(2500);
const st = async () => ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
if (!secs.length) return { err: 'no rw-stack' };
const ai = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本');
const sb = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词');
if (!ai || !sb) return { err: 'missing col', heads: secs.map((s) => s.querySelector('.rw-sec-head')?.textContent.trim()) };
const aiBody = ai.querySelector('.rw-sec-body'); const sbBody = sb.querySelector('.rw-sec-body');
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
return {
aiScrollTop: Math.round(aiBody.scrollTop), sbScrollTop: Math.round(sbBody.scrollTop),
aiHeads: aiBody.querySelectorAll('strong').length, sbBlocks: sbBody.querySelectorAll('.sb-block').length,
sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null,
};
})()`);
const init = await st();
console.log('初始:', JSON.stringify(init));
// 翻到有分镜的 AI 脚本版本(rewrite 22)
if ((init.sbBlocks || 0) === 0) {
for (let i = 0; i < 5; i++) {
const hasNext = await ev(`(() => { const b = document.querySelector('[data-vnext]'); if (!b || b.disabled) return false; b.click(); return true; })()`);
if (!hasNext) break;
await sleep(900);
const s = await st();
console.log('翻版本后:', JSON.stringify(s));
if ((s.sbBlocks || 0) > 0) break;
}
}
const ready = await st();
console.log('就绪:', JSON.stringify(ready));
if (!(ready.sbBlocks > 0)) { console.log('ERR: 无分镜版本可验证'); process.exit(0); }
// ===== 链路①:AI脚本列滚动 → 分镜列应对齐(设置scrollTop+补发scroll事件模拟滚动) =====
const setAi = async (delta, tag) => {
const r = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
aiBody.scrollTop += ${delta};
aiBody.dispatchEvent(new Event('scroll')); // CDP下scrollTop赋值不触发事件,补发
return Math.round(aiBody.scrollTop);
})()`);
await sleep(350);
const s = await st();
console.log(`${tag}: aiScrollTop=${r} → sbScrollTop=${s.sbScrollTop} sbVis=${s.sbVis}`);
return s;
};
let cur = await setAi(400, '链路① 第1步滚400');
cur = await setAi(400, '链路① 第2步滚400');
cur = await setAi(400, '链路① 第3步滚400');
const link1 = {
aiMoved: Math.abs(cur.aiScrollTop - ready.aiScrollTop) > 50,
sbMoved: Math.abs(cur.sbScrollTop - ready.sbScrollTop) > 50,
};
console.log('链路①判定:', JSON.stringify(link1), link1.aiMoved && link1.sbMoved ? '✅ AI脚本主导联动生效' : '❌ 联动异常');
// ===== 链路②:分镜列滚动 → AI脚本列独立 =====
const beforeSb = await st();
const r2 = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
sbBody.scrollTop += 600;
sbBody.dispatchEvent(new Event('scroll'));
return Math.round(sbBody.scrollTop);
})()`);
await sleep(350);
const afterSb = await st();
console.log(`链路② 分镜列滚600: sbScrollTop=${r2} → aiScrollTop=${afterSb.aiScrollTop} sbVis=${afterSb.sbVis}`);
const link2 = {
sbMoved: Math.abs(afterSb.sbScrollTop - beforeSb.sbScrollTop) > 50,
aiMovedBack: Math.abs(afterSb.aiScrollTop - beforeSb.aiScrollTop) > 5,
};
console.log('链路②判定:', JSON.stringify(link2), link2.sbMoved && !link2.aiMovedBack ? '✅ 分镜列独立滚动,AI脚本列未动' : (link2.sbMoved ? '❌ AI脚本列被反向联动' : '❌ 分镜列未滚动'));
// ===== 链路①b:AI脚本列跳底部 → 分镜列跟随最后场 =====
const jump = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
aiBody.scrollTop = aiBody.scrollHeight;
aiBody.dispatchEvent(new Event('scroll'));
return { set: Math.round(aiBody.scrollTop) };
})()`);
await sleep(400);
const afterJump = await st();
const detail = await ev(`(() => {
const secs = [...document.querySelectorAll('.rw-stack > .rw-sec')];
const aiBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI脚本').querySelector('.rw-sec-body');
const heads = [...aiBody.querySelectorAll('strong, h1, h2, h3, h4')].filter((el) => /场\\s*\\d/.test(el.textContent));
const sbBody = secs.find((s) => s.querySelector('.rw-sec-head')?.textContent.trim() === 'AI分镜提示词').querySelector('.rw-sec-body');
const vis = [...sbBody.querySelectorAll('.sb-block')].find((b) => b.getBoundingClientRect().top >= sbBody.getBoundingClientRect().top - 4);
return { lastAiHead: heads.length ? heads[heads.length - 1].textContent.trim().slice(0, 24) : null, sbVis: vis ? (vis.querySelector('.sb-head')?.textContent.trim() || '').slice(0, 30) : null };
})()`);
console.log('链路①b 跳底部:', JSON.stringify({ jump, sbScrollTop: afterJump.sbScrollTop, detail }));
try { ws.close(); } catch (e) {}
process.exit(0);