SCENE_FONT defaults in config.js point at Debian's font layout, which does not exist in the Alpine runtime image; drawtext only rendered via fontconfig fallback. Pin the Alpine paths in the Dockerfile env. Also add LAST_STATE.md (E2E test results + approved plan for the OBS-style panel rework) and the OBS reference screenshot for the redesign. Co-Authored-By: Claude Code <noreply@anthropic.com>
6.3 KiB
LAST_STATE — 2026-09-02
Session sign-off snapshot. Read this first when resuming the multistreaming work.
1. Done: multistreaming E2E test (podman) — all passing
Tested the whole app in rootless podman: built multistreaming:test from
services/multistreaming/Dockerfile, ran with --network host, AUTH_MODE=local, volume
ms-test-data.
- HTTP API smoke: health, auth config, providers, register/login/session,
/api/me,/api/state. - RTMP ingest: published 12s
testsrc2with host ffmpeg → feed went live in state, cleaned up on unpublish. - Passthrough fanout:
-c copychild pushed to a localffmpeg -listen 1RTMP sink on :1936; sink file probed as h264 1280×720, 12.004s — exact copy, 0 restarts. - Scene composition: grid scene + text overlay activated → passthrough suspended, composed child ran; sink file h264 1920×1080 + aac program audio; drawtext overlay confirmed rendered (white-glyph pixel analysis; fanout log shows the correct filter graph).
- Persistence: accounts/rooms/feeds survived container recreation (volume).
Scratch scripts: /tmp/ms_test/ (passthrough.sh, scene.sh, verify-fix.sh, ids.txt…).
Test recipe in Claude memory: memory/multistreaming-podman-test.md.
Test container ms-test may still be running (panel :8080); teardown:
podman rm -f ms-test && podman volume rm -f ms-test-data.
2. Done: Dockerfile font path fix (committed)
src/config.js defaults SCENE_FONT/SCENE_FONT_BOLD to Debian's
/usr/share/fonts/truetype/dejavu/, which does not exist in the Alpine image (fonts live in
/usr/share/fonts/dejavu/). drawtext only worked via FFmpeg's fontconfig fallback.
Fixed by setting the Alpine paths as ENV in services/multistreaming/Dockerfile. Rebuilt,
re-verified (fanout log shows the correct fontfile, overlay still renders).
3. In progress: OBS-style rework of the web panel (services/multistreaming/web)
Reference image: reference1.png (repo root) — an OBS Studio screenshot:
left dock Scenes+Sources, center program canvas, right dock Audio Mixer + Scene Transitions +
Controls, bottom status bar. (Image viewing is broken in this environment; the layout was decoded
via OCR.)
User-approved target layout — variant B:
┌──────────┬──────────────────────┬───────────────┐
│ SCENES │ │ CONTROLS │
│ │ PROGRAM PREVIEW │ Start/Stop │
│ SOURCES │ (live feed) ├───────────────┤
│ (media) │ │ Rooms/Vaults │
│ ├──────────────────────┤ User/Members │
│ │ MIXER │ TRANSITIONS│ │
└──────────┴──────────────────────┴───────────────┘
- Left dock: Scenes + Sources (media feeds) + Destinations (output routing) + Images.
- Center: simulated 16:9 program canvas; beneath it Audio Mixer + Scene Transitions panels; OBS-style slim status bar at the bottom.
- Right sidebar: Streaming Controls + app nav (rooms/vaults/user, moved from the left) +
Members. The current left
AppSidebarand rightRoomSidebarare both absorbed into this one right sidebar. - Simulated program canvas (no real video pipeline): render from scene data — grid/PiP layout
math mirroring backend
src/compose.js(grid cell = floor(W/cols) × floor(H/rows), single live feed promoted fullscreen, PiP main fullscreen + 480×270 window top-right); slot boxes with feed names + LIVE badges; text/image overlays at their real coordinates scaled to the canvas (text: x,y,fontSize,color,bold; image: x,y,width,height,opacity, src/api/images/:id/file). Passthrough mode (no active scene): live feeds in a simple grid. Scene activate crossfades the canvas (CSS transition keyed onactiveSceneId).
Design decisions already made:
- Mixer: active scene → one channel per scene-output with functional audio-routing control
(Program/Silent/Feed via
api.updateSceneOutput) + animated meter whenruntime.running. Passthrough → channels = live feeds, decorative animated meters only (no real levels exist). - Transitions: "Fade" + duration slider (localStorage, client-side only) controlling the CSS crossfade of the simulated canvas on scene activate. Real video crossfade = future backend work.
- Controls: Start Streaming (existing
GrantDialogflow), Stop Streaming (wire the currently unusedapi.stopStream— zero call sites today), per-account grant/live status. No Studio Mode / Recording / Virtual Camera (no backend equivalents). - Right sidebar: Controls + NavVaults + NavRooms + NavUser + Members; renders vaults/rooms/user even without a room open (Controls/Members room-only).
- HomeView unchanged, just rendered in the center column without a left sidebar.
- Keep prop-drilling + 3s
api.state()polling (src/lib/use-app.ts); no state rewrite. - SceneEditorDialog / ImageUploader / PromptDialogs stay dialogs; only mounting points move.
Current codebase facts (from exploration):
- Shell:
src/App.tsx(SidebarProvider > AppSidebar + SidebarInset{header+RoomView|HomeView} + RoomSidebar; global dialogs at App level). No router; nav via useAppscreen/roomId. - Components:
app-sidebar.tsx,room-sidebar.tsx,room-view.tsx(stacked cards),scene-panel.tsx,scene-editor.tsx(606-line dialog),dialogs.tsx(GrantDialog 428-515),image-upload.tsx,home-view.tsx,nav-*.tsx,theme-provider.tsx(useThemeunused in UI). - shadcn ui/ has sidebar/card/dialog/select/switch/toggle-group/badge/tooltip etc.
Missing: slider (build
ui/slider.tsxfrom theradix-uiunified package already in dependencies — no new npm deps). lib/api.tshas every endpoint needed;lib/types.tscovers all state shapes.- Dark theme tokens are neutral gray (
src/index.css); OBS look is dark blue-gray — subtle token tint planned.
What remains: implement the layout (no code written yet). The full implementation plan
(component-by-component, canvas math, crossfade pattern, styling tokens, order, risks) is in
~/.claude/plans/starry-tickling-grove.md.