diff --git a/LAST_STATE.md b/LAST_STATE.md new file mode 100644 index 0000000..cb51b4a --- /dev/null +++ b/LAST_STATE.md @@ -0,0 +1,99 @@ +# 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 `testsrc2` with host ffmpeg → feed went live in state, cleaned up on unpublish. +- Passthrough fanout: `-c copy` child pushed to a local `ffmpeg -listen 1` RTMP 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 `AppSidebar` and right `RoomSidebar` are 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 on `activeSceneId`). + +**Design decisions already made:** + +1. **Mixer**: active scene → one channel per scene-output with functional audio-routing control + (Program/Silent/Feed via `api.updateSceneOutput`) + animated meter when `runtime.running`. + Passthrough → channels = live feeds, decorative animated meters only (no real levels exist). +2. **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. +3. **Controls**: Start Streaming (existing `GrantDialog` flow), **Stop Streaming** (wire the + currently unused `api.stopStream` — zero call sites today), per-account grant/live status. + No Studio Mode / Recording / Virtual Camera (no backend equivalents). +4. **Right sidebar**: Controls + NavVaults + NavRooms + NavUser + Members; renders vaults/rooms/user + even without a room open (Controls/Members room-only). +5. **HomeView** unchanged, just rendered in the center column without a left sidebar. +6. Keep prop-drilling + 3s `api.state()` polling (src/lib/use-app.ts); no state rewrite. +7. 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 useApp `screen/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` (`useTheme` unused in UI). +- shadcn ui/ has sidebar/card/dialog/select/switch/toggle-group/badge/tooltip etc. + **Missing: slider** (build `ui/slider.tsx` from the `radix-ui` unified package already in + dependencies — no new npm deps). +- `lib/api.ts` has every endpoint needed; `lib/types.ts` covers 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`. diff --git a/reference1.png b/reference1.png new file mode 100644 index 0000000..5e7dd7c Binary files /dev/null and b/reference1.png differ diff --git a/services/multistreaming/Dockerfile b/services/multistreaming/Dockerfile index 47f9b12..c1ce9d9 100644 --- a/services/multistreaming/Dockerfile +++ b/services/multistreaming/Dockerfile @@ -35,9 +35,14 @@ COPY --from=web /web/dist ./web/dist # volume is writable without root. RUN mkdir -p /data && chown node:node /data VOLUME ["/data"] +# Font paths must match where Alpine's font-dejavu installs them (the config +# defaults use Debian's /usr/share/fonts/truetype/dejavu layout, which does not +# exist here; drawtext only worked via a fontconfig fallback). ENV DATA_DIR=/data \ RTMP_PORT=1935 \ - HTTP_PORT=8080 + HTTP_PORT=8080 \ + SCENE_FONT=/usr/share/fonts/dejavu/DejaVuSans.ttf \ + SCENE_FONT_BOLD=/usr/share/fonts/dejavu/DejaVuSans-Bold.ttf EXPOSE 1935 8080