Skill: flyingzl/browser-session-manager (version 1.0.0)

# 即梦AI (Jimeng) 视频自动化生成完全指南 > **TL;DR**: 本文记录如何通过 Playwright + 浏览器 Session 管理，实现即梦AI视频生成的全自动化。包含图片压缩、Cookie 注入、表单填写、视频提交等完整流程。 --- ## 📋 背景 即梦AI (jimeng.jianying.com) 是字节跳动旗下的 AI 视频生成平台。官方提供了 Web 界面，但没有开放 API。当我们需要批量生成视频或集成到工作流时，就需要通过浏览器自动化来实现。 ## 🎯 核心挑战 1. **登录状态保持**: 需要正确处理 Cookies 和 LocalStorage 2. **文件上传**: 需要压缩图片到合适大小（WebP 格式最佳） 3. **动态表单**: 需要等待元素加载、处理弹窗 4. **提交检测**: 需要检测页面跳转和生成状态 ## 🛠️ 技术栈 - **Playwright**: 浏览器自动化框架 - **ImageMagick**: 图片压缩（convert 命令） - **Node.js**: 脚本运行环境 --- ## 📁 目录结构 ``` workspace/ ├── screenshots/ │ └── YYYYMMDD_HHMMSS/ # 按时间戳组织的截图 │ ├── 01_initial.png │ ├── 02_start_uploaded.png │ └── ... ├── skills/ │ ├── browser-session-manager/ # Session 管理 Skill │ └── web-form-automation/ # 表单自动化 Skill └── session-data.json # Cookie 数据文件 ``` --- ## 🚀 完整实现步骤 ### 1. 准备阶段：图片压缩 即梦对上传图片有大小限制，必须先压缩： ```bash # 转换为 WebP 格式，压缩到 30-50KB convert start.png start.webp convert end.png end.webp # 验证大小 ls -lh *.webp # -rw-r--r-- 1 node node 88K Feb 15 14:59 start.webp # -rw-r--r-- 1 node node 54K Feb 15 14:59 end.webp ``` **⚠️ 重要**: PNG 原图可能 4MB+，直接上传会失败。WebP 格式可压缩 99% 且画质损失极小。 --- ### 2. Session 数据准备 从浏览器导出 Cookie 和 LocalStorage，保存为 JSON： ```json { "exportTime": "2026-02-15T14:55:58.374Z", "url": "https://jimeng.jianying.com/ai-tool/home?type=video", "hostname": "jimeng.jianying.com", "cookies": [ { "name": "sessionid", "value": "xxx", "domain": ".jianying.com", "path": "/", "secure": false, "httpOnly": true, "sameSite": "unspecified" } ], "localStorage": { [REDACTED_SECRET]: "\"dreamina_seedance_40_pro\"", "DREAMINA_THEME": "light" }, "sessionStorage": {} } ``` **获取方法**: 使用浏览器扩展（如 "Cookie-Editor"）导出，或从 DevTools Application 面板复制。 --- ### 3. 核心自动化脚本 ```javascript const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); async function generateVideo(sessionFile, startImage, endImage, prompt, screenshotDir) { // 读取 Session 数据 const sessionData = JSON.parse(fs.readFileSync(sessionFile, 'utf8')); // 启动浏览器 const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] }); const context = await browser.newContext(); const page = await context.newPage(); // ===== 关键步骤 1: 设置 Cookies ===== if (sessionData.cookies) { const cookiesByDomain = {}; for (const cookie of sessionData.cookies) { // 修复 sameSite 值 let sameSite = cookie.sameSite; if (sameSite === 'unspecified') sameSite = 'Lax'; if (sameSite === 'no_restriction') sameSite = 'None'; const fixedCookie = { ...cookie, sameSite }; const domain = cookie.domain || '.jianying.com'; if (!cookiesByDomain[domain]) cookiesByDomain[domain] = []; cookiesByDomain[domain].push(fixedCookie); } for (const [domain, cookies] of Object.entries(cookiesByDomain)) { try { await context.addCookies(cookies); } catch (e) { console.log(`Cookie 设置失败: ${e.message}`); } } } // ===== 关键步骤 2: 导航并注入 LocalStorage ===== await page.goto('https://jimeng.jianying.com/ai-tool/home?type=video', { waitUntil: 'domcontentloaded', timeout: 60000 }); await page.waitForTimeout(3000); // 注入 LocalStorage await page.evaluate((data) => { for (const [key, value] of Object.entries(data.localStorage || {})) { try { localStorage.setItem(key, value); } catch (e) {} } }, sessionData); // 刷新使存储生效 await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForTimeout(3000); // ===== 关键步骤 3: 处理登录弹窗 ===== try { const agreeBtn = await page.getByText('同意').first(); if (await agreeBtn.isVisible({ timeout: 3000 })) { await agreeBtn.click(); await page.waitForTimeout(2000); } } catch (e) { // 没有弹窗，继续 } // ===== 关键步骤 4: 上传首帧图片 ===== const startBtn = await page.getByText('首帧').first(); await startBtn.click(); await page.waitForTimeout(1500); const fileInput = await page.locator('input[type="file"]').first(); await fileInput.setInputFiles(startImage); await page.waitForTimeout(3000); // 等待上传完成 // ===== 关键步骤 5: 上传尾帧图片 ===== const endBtn = await page.getByText('尾帧').first(); await endBtn.click(); await page.waitForTimeout(1500); const fileInputs = await page.locator('input[type="file"]').all(); await fileInputs[fileInputs.length - 1].setInputFiles(endImage); await page.waitForTimeout(3000); // ===== 关键步骤 6: 选择模型 ===== const modelBtn = await page.getByText('Seedance 2.0 Fast').first(); await modelBtn.click(); await page.waitForTimeout(2000); // 选择 Seedance 2.0（非 Fast 版本） const seedance20 = await page.getByText('Seedance 2.0').nth(1); await seedance20.click(); await page.waitForTimeout(1500); // ===== 关键步骤 7: 设置时长为 15s ===== const durationBtn = await page.getByText('5s').first(); await durationBtn.click(); await page.waitForTimeout(1500); const fifteen = await page.getByText('15s').first(); await fifteen.click(); await page.waitForTimeout(1500); // ===== 关键步骤 8: 输入提示词 ===== const textarea = await page.locator('textarea').first(); await textarea.click(); // 使用 pressSequentially 模拟真实打字，触发输入事件 await textarea.pressSequentially(prompt, { delay: 30 }); await page.waitForTimeout(3000); // ===== 关键步骤 9: 提交生成 ===== const submit = await page.locator('button[class*="submit"]').first(); await submit.click({ force: true }); // force: true 确保能点击 await page.waitForTimeout(2000); // ===== 关键步骤 10: 检测页面跳转 ===== await page.waitForTimeout(3000); const currentUrl = page.url(); if (currentUrl.includes('/generate')) { console.log('✅ 成功跳转到生成页面'); } // 等待 5 秒查看生成状态 await page.waitForTimeout(5000); await page.screenshot({ path: path.join(screenshotDir, 'final.png') }); await browser.close(); } ``` --- ## ⚠️ 踩坑记录 ### 1. Cookie sameSite 问题 **错误**: `browserContext.addCookies: cookies[3].sameSite: expected one of (Strict|Lax|None)` **解决**: 导出时 `sameSite:


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (HIGH)