Files
clh02467605 4c51c95f31 feat(video): switch default video model to wan3.0-video and adapt All-in-One support
- Update default models for video generate/ref and config UI to wan3.0-video
- Add --file file-to-video support for wan3.0; route kf2v through media[] instead of image2video
- Emit reference voices as standalone reference_audio entries on wan3.0 ref
- Sync e2e/stress tests and bailian-gen reference docs
2026-08-24 18:00:52 +08:00

124 lines
3.9 KiB
JavaScript
Raw Permalink 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.
#!/usr/bin/env node
/**
* `video ref` 参考生视频并发压测;使用前置图片。
*/
import { join } from "node:path";
import { defineStressTarget } from "../lib/define-stress-target.mjs";
import { parseVideoResult, extractError } from "../lib/parsers.mjs";
import { escapeHtml, escapeTableCell, getErrorMessage } from "../lib/report.mjs";
import { optFrom } from "../lib/argv-parse.mjs";
const prompts = [
"图1在草地上缓慢行走远景静态镜头。",
"图1微微转头望向镜头背景保持稳定。",
"图1在柔和光线下眨眼微风拂动发丝。",
];
export const runStress = defineStressTarget({
canonical: "video-ref",
defaultModel: "wan3.0-video",
batchDirPrefix: "video-ref-batch",
helpText: "pnpm run test:stress -- video-ref [--reuse-fixtures] -- --count 5 -c 2",
defaultTimeoutMs: 3_600_000,
minTimeoutMs: 60_000,
defaultRateLimitMax: 10,
defaultRateLimitWindowMs: 1000,
defaultRetryBaseMs: 15000,
defaultMaxRetries: 2,
hasPollInterval: true,
defaultPollInterval: 15,
fixtureKind: "image",
fixtureSetupTimeoutMs: 600_000,
videoSetupTimeoutMs: 3_600_000,
resolveFixtureRef: (prerequisites) =>
prerequisites.image?.primaryUrl ||
prerequisites.image?.urls?.[0] ||
prerequisites.image?.saved?.[0],
fixtureRefErrorMessage: "前置 manifest 缺少参考图",
extraParams: (ARGV) => ({
DURATION: Math.max(2, Math.min(10, parseInt(optFrom(ARGV, "DURATION") ?? "5", 10) || 5)),
}),
generatePrompt: (idx) => `${prompts[idx % prompts.length]} [#r2v-${idx}]`,
buildCliArgs: ({
MODEL,
prompt,
runDir,
fixtureRef,
CLI_TIMEOUT_SEC,
POLL_INTERVAL,
extraParams,
index,
}) => [
"video",
"ref",
"--model",
MODEL,
"--prompt",
prompt,
"--image",
String(fixtureRef),
"--download",
join(runDir, `ref_${String(index + 1).padStart(3, "0")}.mp4`),
"--duration",
String(extraParams.DURATION),
"--output",
"json",
"--timeout",
String(CLI_TIMEOUT_SEC),
"--poll-interval",
String(POLL_INTERVAL),
],
buildBaseRecord: ({ runDir, index }) => ({
downloadPath: join(runDir, `ref_${String(index + 1).padStart(3, "0")}.mp4`),
}),
parseStdout: (stdout) => Promise.resolve(parseVideoResult(stdout)),
extraReportMeta: ({ fixtureRef, extraParams }) => ({
extraMdLines: [`- **参考图**: ${String(fixtureRef)}`, `- **时长**: ${extraParams.DURATION}s`],
extraHtmlMeta: ` · ref · ${extraParams.DURATION}s`,
}),
reportSpec: {
titleMd: "参考生视频批量压测报告video ref",
titleHtml: "参考生视频批量压测报告video ref",
promptColumnMd: "Prompt",
promptColumnHtml: "Prompt",
outcomeColumnMd: "视频 / 错误",
outcomeColumnHtml: "视频 / 错误",
formatOutcomeMd: (r) => {
if (r.status === "success") {
const lines = [];
if (r.videoUrls?.length) lines.push(...r.videoUrls.map((u) => escapeTableCell(u)));
if (r.saved?.length) lines.push(...r.saved.map((p) => escapeTableCell(`(本地) ${p}`)));
return lines.length ? lines.join("<br>") : "—";
}
return escapeTableCell(getErrorMessage(r, extractError));
},
formatOutcomeHtml: (r) => {
if (r.status === "success") {
const parts = [];
if (r.videoUrls?.length) {
for (const url of r.videoUrls) {
const safe = escapeHtml(url);
parts.push(
`<div class="outcome-success"><a class="url-link" href="${safe}" target="_blank" rel="noopener">${safe}</a></div>`,
);
}
}
if (r.saved?.length) {
for (const p of r.saved) parts.push(`<code class="path">${escapeHtml(p)}</code>`);
}
return parts.length ? parts.join("") : "—";
}
return `<span class="outcome-error">${escapeHtml(getErrorMessage(r, extractError))}</span>`;
},
},
});