实现 video batch 自动清理与按需标注视频,并补充子进程调用测试。

batch 完成后仅保留数据库文本结果,勾选时才生成临时标注视频(24h TTL);新增 FastAPI 到 reference bundle 与 algorithm_runner 的单元测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin
2026-05-21 16:30:48 +08:00
parent e6434d0bb6
commit 09885b4184
10 changed files with 2047 additions and 1354 deletions

View File

@@ -324,7 +324,13 @@
<p class="small muted" style="margin:8px 0 0">
<label style="display:inline-flex;align-items:flex-start;gap:8px;cursor:pointer;max-width:52rem">
<input type="checkbox" id="video-batch-mode" style="margin-top:2px" />
<span><strong>非实时精确模式</strong>:使用调试区「路 1」上传的完整 MP4调用主 FastAPI 的 <code>POST /internal/demo/video-batch-surgery</code>,复用 <code>algorithm_subprocesses/5.15/main.py</code> 并在完成后生成带标签视频。该模式不会启动 RTSP 实时会话,处理完成后用查询结果接口查看。</span>
<span><strong>非实时精确模式</strong>:使用调试区「路 1」上传的完整 MP4调用主 FastAPI 的 <code>POST /internal/demo/video-batch-surgery</code>,复用 <code>algorithm_subprocesses/5.15/main.py</code>。该模式不会启动 RTSP 实时会话,处理完成后用查询结果接口查看文本明细</span>
</label>
</p>
<p class="small muted" style="margin:8px 0 0">
<label style="display:inline-flex;align-items:flex-start;gap:8px;cursor:pointer;max-width:52rem">
<input type="checkbox" id="video-batch-include-vis" style="margin-top:2px" />
<span><strong>生成标注视频</strong>临时预览24 小时内可播放;需同时勾选「非实时精确模式」)。</span>
</label>
</p>
<div class="actions">
@@ -532,6 +538,22 @@
el.innerHTML = `<strong>识别医生:</strong>${text}`;
}
function hideVideoBatchVisualization() {
const wrap = $("video-batch-vis");
const player = $("video-batch-vis-player");
const hint = $("video-batch-vis-hint");
if (wrap) wrap.style.display = "none";
if (player) {
player.removeAttribute("src");
player.load();
}
if (hint) hint.textContent = "";
}
function videoBatchIncludeVis() {
return $("video-batch-include-vis") && $("video-batch-include-vis").checked;
}
function showVideoBatchVisualization(sid, urlPath, doctorDisplay) {
const wrap = $("video-batch-vis");
const player = $("video-batch-vis-player");
@@ -720,6 +742,7 @@
fd.append("surgery_id", sid);
fd.append("video1", f, f.name);
fd.append("candidate_consumables_json", JSON.stringify(candidateConsumables));
fd.append("include_visualization", videoBatchIncludeVis() ? "true" : "false");
const { res, body } = await apiVideoBatch(fd);
if (!res.ok) {
const detail = (body && (body.detail !== undefined)) ? body.detail : body;
@@ -728,7 +751,11 @@
return;
}
lastVideoBatchDoctorDisplay = (body && body.doctor_display) || "";
showVideoBatchVisualization(sid, body && body.visualization_url, lastVideoBatchDoctorDisplay);
if (videoBatchIncludeVis() && body && body.visualization_url) {
showVideoBatchVisualization(sid, body.visualization_url, lastVideoBatchDoctorDisplay);
} else {
hideVideoBatchVisualization();
}
await apiJson("GET", `/client/surgeries/${sid}/result`);
return;
}
@@ -803,8 +830,10 @@
$("btn-result").onclick = async () => {
const sid = ensureSurgeryId();
if (!sid) return;
if ($("video-batch-mode") && $("video-batch-mode").checked) {
if ($("video-batch-mode") && $("video-batch-mode").checked && videoBatchIncludeVis()) {
showVideoBatchVisualization(sid, null);
} else {
hideVideoBatchVisualization();
}
const { res, body } = await apiJson("GET", `/client/surgeries/${sid}/result`);
const target = $("result-render");