Files
AiVideo/server/public/index.html
2026-03-18 17:36:07 +08:00

124 lines
4.2 KiB
HTML
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.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>AiVideo POC - Script Stream Test</title>
<style>
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "PingFang SC", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif; margin: 24px; }
.row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
input[type="text"] { width: min(900px, 100%); padding: 10px 12px; font-size: 16px; }
button { padding: 10px 14px; font-size: 16px; cursor: pointer; }
pre { background: #0b1020; color: #d7e1ff; padding: 14px; border-radius: 10px; overflow: auto; }
.scenes { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; margin-top: 12px; }
.card { border: 1px solid #e5e7eb; border-radius: 10px; padding: 12px; }
.k { color: #6b7280; font-size: 12px; margin: 8px 0 2px; }
.v { white-space: pre-wrap; }
.muted { color: #6b7280; }
</style>
</head>
<body>
<h2>AiVideo POC实时分镜脚本流测试</h2>
<p class="muted">点击运行后,页面会通过 SSE 实时接收 Python stdout并把分镜渲染到下方。</p>
<div class="row">
<input id="prompt" type="text" value="写一个温暖的城市夜景故事" />
<label class="row" style="gap:6px;">
<input id="mock" type="checkbox" checked />
mock无 ComfyUI / 无 Key 也能跑)
</label>
<button id="run">运行</button>
<button id="stop">停止</button>
</div>
<div class="scenes" id="scenes"></div>
<h3>原始日志stdout/stderr</h3>
<pre id="log"></pre>
<script>
const $ = (id) => document.getElementById(id);
const logEl = $("log");
const scenesEl = $("scenes");
let es = null;
function log(line) {
logEl.textContent += line + "\n";
logEl.scrollTop = logEl.scrollHeight;
}
function upsertScene(scene) {
const id = "scene-" + scene.index;
let card = document.getElementById(id);
if (!card) {
card = document.createElement("div");
card.className = "card";
card.id = id;
scenesEl.appendChild(card);
}
card.innerHTML = `
<div><strong>Scene ${scene.index}</strong></div>
<div class="k">image_prompt</div><div class="v">${escapeHtml(scene.image_prompt)}</div>
<div class="k">video_motion</div><div class="v">${escapeHtml(scene.video_motion || "")}</div>
<div class="k">narration</div><div class="v">${escapeHtml(scene.narration)}</div>
`;
}
function escapeHtml(s) {
return String(s)
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
function start() {
stop();
logEl.textContent = "";
scenesEl.innerHTML = "";
const prompt = $("prompt").value.trim();
const mock = $("mock").checked ? "1" : "0";
if (!prompt) return;
const url = `/api/run?prompt=${encodeURIComponent(prompt)}&mock=${mock}`;
es = new EventSource(url);
es.addEventListener("status", (e) => log("[status] " + e.data));
es.addEventListener("stderr", (e) => log("[stderr] " + e.data));
es.addEventListener("done", (e) => {
log("[done] exit_code=" + e.data);
stop();
});
es.addEventListener("line", (e) => {
const line = e.data;
log(line);
if (line.startsWith("SCENE_JSON ")) {
try {
const obj = JSON.parse(line.slice("SCENE_JSON ".length));
upsertScene(obj);
} catch (err) {
log("[parse_error] " + err);
}
}
});
es.onerror = () => {
log("[error] connection error");
};
}
function stop() {
if (es) {
es.close();
es = null;
}
}
$("run").addEventListener("click", start);
$("stop").addEventListener("click", stop);
</script>
</body>
</html>