feat: 新增文件

This commit is contained in:
Daniel
2026-03-18 17:36:07 +08:00
commit f99098ec58
702 changed files with 68533 additions and 0 deletions

123
server/public/index.html Normal file
View File

@@ -0,0 +1,123 @@
<!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>