Files

66 lines
3.4 KiB
JavaScript

// 二期第5段端到端:上传视频 → 拆解稿 → 点确定 → 真出结构化脚本;并验「来源」记成「上传视频提炼」
import { chromium } from "playwright";
import { mkdirSync, existsSync } from "node:fs";
const BASE = process.env.BASE || "http://127.0.0.1:5173";
const API = process.env.API || "http://127.0.0.1:8010";
const OUT = process.argv[2] || "shots-seg5";
const VIDEO = process.env.VIDEO || "/tmp/_probe_tshirt.mp4";
mkdirSync(OUT, { recursive: true });
if (!existsSync(VIDEO)) throw new Error(`测试视频不存在: ${VIDEO}`);
const login = await (await fetch(`${API}/api/auth/login/`, {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "airshelf", password: "Restraint2026" })
})).json();
const tok = login.token;
const auth = { Authorization: `Token ${tok}`, "Content-Type": "application/json" };
const prods = (await (await fetch(`${API}/api/products/`, { headers: auth })).json()).results || [];
const target = await (await fetch(`${API}/api/projects/`, {
method: "POST", headers: auth,
body: JSON.stringify({ name: `二期第5段E2E-${Date.now() % 100000}`, product: prods[0].id })
})).json();
console.log("project:", target.id, "| product:", prods[0].title);
const browser = await chromium.launch();
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2 });
await ctx.addInitScript((t) => localStorage.setItem("airshelf_token", t), tok);
const page = await ctx.newPage();
page.on("pageerror", (e) => console.error(" pageerror:", e.message));
await page.goto(`${BASE}/pipeline/${target.id}?st=1#stage-1`, { waitUntil: "networkidle", timeout: 40000 });
await page.waitForTimeout(2000);
const chooser = page.waitForEvent("filechooser");
await page.locator('.chat-mode[data-mode="video"]').click();
await (await chooser).setFiles(VIDEO);
await page.locator(".chat-attach-row .chip").first().waitFor({ timeout: 180000 });
console.log("拆解稿字数:", (await page.locator("#chat-textarea").inputValue()).length);
// 时长跟参考视频对齐(22 秒 → 20 秒档),再点确定生成
await page.locator(".setup-card .setup-select").nth(3).selectOption("20");
await page.locator(".setup-card .btn-primary").click();
console.log("已点确定,等脚本生成…");
await page.locator(".shot-card, .shot-row").first().waitFor({ timeout: 240000 });
await page.waitForTimeout(1500);
const project = await (await fetch(`${API}/api/projects/${target.id}/`, { headers: auth })).json();
const script = (project.script_versions || []).find((v) => v.is_adopted) || (project.script_versions || [])[0];
console.log("脚本来源(后端记录):", script?.source);
console.log("镜数:", script?.segments?.length, "| 各镜时长:", (script?.segments || []).map((s) => s.duration_seconds).join("/"));
console.log("总时长:", (script?.segments || []).reduce((a, s) => a + s.duration_seconds, 0));
console.log("第1镜旁白:", (script?.segments || [])[0]?.narration);
console.log("第1镜画面:", ((script?.segments || [])[0]?.visual_prompt || "").slice(0, 90));
const brief = await page.locator("#brief-source").textContent();
console.log("来源徽标(前端):", brief);
await page.screenshot({ path: `${OUT}/5-generated-script.png`, fullPage: false });
console.log("shot 5-generated-script");
await browser.close();
await fetch(`${API}/api/projects/${target.id}/`, { method: "DELETE", headers: auth });
console.log("DONE");