feat(webui): render video media attachments

Add signed media URLs to live WebSocket replies and teach the WebUI to classify and render video attachments, so bot-sent videos can play inline in both live chats and session history.

Made-with: Cursor
This commit is contained in:
Xubin Ren
2026-04-25 03:20:40 +08:00
committed by Xubin Ren
parent be05189f39
commit e52fe2a8e2
10 changed files with 327 additions and 15 deletions
+33
View File
@@ -190,6 +190,39 @@ async def test_send_delivers_json_message_with_media_and_reply() -> None:
assert payload["media"] == ["/tmp/a.png"]
@pytest.mark.asyncio
async def test_send_stages_external_media_as_signed_url(monkeypatch, tmp_path) -> None:
bus = MagicMock()
media_root = tmp_path / "media"
ws_media = media_root / "websocket"
ws_media.mkdir(parents=True)
external = tmp_path / "clip.mp4"
external.write_bytes(b"video")
def fake_media_dir(channel: str | None = None):
return ws_media if channel == "websocket" else media_root
monkeypatch.setattr("nanobot.channels.websocket.get_media_dir", fake_media_dir)
channel = WebSocketChannel({"enabled": True, "allowFrom": ["*"]}, bus)
mock_ws = AsyncMock()
channel._attach(mock_ws, "chat-1")
await channel.send(
OutboundMessage(
channel="websocket",
chat_id="chat-1",
content="video",
media=[str(external)],
)
)
payload = json.loads(mock_ws.send.call_args[0][0])
assert payload["media"] == [str(external)]
assert payload["media_urls"][0]["name"] == "clip.mp4"
assert payload["media_urls"][0]["url"].startswith("/api/media/")
assert any(p.name.endswith("-clip.mp4") for p in ws_media.iterdir())
@pytest.mark.asyncio
async def test_send_missing_connection_is_noop_without_error() -> None:
bus = MagicMock()