Phase 12 verified live; MediaMTX config changes documented as restart-only on this setup

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 16:54:08 -05:00
parent f1d19ba32d
commit 6db786e00e
4 changed files with 16 additions and 12 deletions

View File

@@ -287,7 +287,7 @@ Discord roles); the publish path must start with the user's own name.
- [ ] dock + README; remove streamers from configure.py; delete `streamers.json`
- [ ] test: OBS with an app password; Go Live with no key; a non-streamer gets denied
### Phase 12 — frame-accurate timecode for multicam via an overscan band (built; deploy pending)
### Phase 12 — frame-accurate timecode for multicam via an overscan band ✅ live, verified (OBS: strip, −3 ms drift, 0 gaps)
Target: clips from several streamers align to the frame (16.7 ms @60) on a multicam timeline; the game
image is untouched and finals carry nothing. Reference clock = the server; streamer PC clocks are never trusted.

View File

@@ -289,9 +289,10 @@ MediaMTX asks dumpd (`POST /mtx-auth` on 127.0.0.1:8081, reachable only from Doc
connection. Reads/playback are allowed (viewers are gated by Caddy + Authentik). Publishing needs a token
minted by `GET /me/stream-token` behind Authentik for a member of `streamers`/`admins`/`media-admins`; dumpd
checks the HMAC signature (`/etc/vanguard/stream-token.secret`, generated into `.env` as `STREAM_TOKEN_SECRET`),
the expiry, and that the path starts with that user's name. MediaMTX hot-reloads paths/recording settings
but **not** the auth section: after changing auth, `docker compose restart mediamtx` between streams.
`configure.py` reloads Caddy in place and restarts only copyparty.
the expiry, and that the path starts with that user's name. MediaMTX does not pick up `mediamtx.yml` changes
live on this setup: after any change to it, `docker compose restart mediamtx` between streams. Day-to-day
operations (streamers, tokens, titles) never touch that file. `configure.py` reloads Caddy in place and
restarts only copyparty.
## Timecode for multicam editing (frame accurate)

View File

@@ -25,7 +25,7 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea
| Who | Activity | Where | Login |
|---|---|---|---|
| Streamer | One-time: dock → Timecode band → "Add band + clock source" (OBS) or leave the Go Live checkbox on — gives editors frame-accurate timecode | OBS dock / Go Live | — **pending deploy** |
| Streamer | One-time: dock → Timecode band → "Add band + clock source" (OBS) or leave the Go Live checkbox on — gives editors frame-accurate timecode | OBS dock / Go Live | live |
| Streamer | Type a title (live preview + naming rules), Go Live, Stop. "Dump tonight" if it was junk (3-day undo). Manual-setup accordion with endpoints and a hidden token | OBS browser dock `https://ovh1…/dock/` | Discord login in the dock; dock mints a 12 h token per Go Live — **no passwords, verified live** |
| Gamer | Nothing; clips save where they always did | their clipper + Syncthing, paired once | — |
| Editor | Multicam: drop clips on a timeline, sync by the embedded timecode track (frame-accurate when the band was on), cross-check with voice audio; export at the game resolution to drop the band | Resolve / Premiere | — |
@@ -47,8 +47,8 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea
|---|---|---|
| MediaMTX | RTMP/SRT ingest, records fMP4; publish auth delegated to dumpd (tokens via `/mtx-auth`) | live, token auth verified |
| copyparty | browse/scrub/download/finals upload | behind Authentik, group permissions (`editors`, `media-admins`), Discord-only, no WebDAV | live |
| dumpd + dock | dump, stream tokens, MediaMTX auth hook, `/time`; OBS dock with timecode-band setup | identity from Authentik headers | live; band/timecode **pending deploy** |
| Timecode | `bin/vgstrip.py` decoder, `tmcd` + sidecar in the sweep, `/dock/clock/` strip page, Go Live band composite, portal crop | server clock is the reference | **built, pending deploy** |
| dumpd + dock | dump, stream tokens, MediaMTX auth hook, `/time`; OBS dock with timecode-band setup | identity from Authentik headers | live |
| Timecode | `bin/vgstrip.py` decoder, `tmcd` + sidecar in the sweep, `/dock/clock/` strip page, Go Live band composite, portal crop | server clock is the reference | **live, verified** (OBS: frame-accurate, 0 gaps) |
| Syncthing | clip intake, UI at `/sync/` | UI behind Authentik + `media-admins` | live |
| normalize sweep | editing standard, one copy per clip | live |
| nftables | host firewall (22, 80/443, 1935, 2222, 3306, 8890/udp, 22000) | live |
@@ -65,7 +65,7 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea
## Unfinished, in rough priority
0. **Deploy phase 12** (timecode): vgstrip + sweep + dumpd `/time` + configure (Caddy `/time` routes, MediaMTX `%f`) + dock/clock + portal. Then a streamer adds the band and we check a recording's `tmcd` and sidecar.
0. `docker compose restart mediamtx` at a quiet moment so recording filenames gain microseconds (the arrival-time fallback); everything else in phase 12 is live.
1. Authentik: `discord-roles-to-groups` policy so `streamers`/`editors`/`media-admins`/`bot-users` follow Discord roles (`bot-users` group needed for the bot host). Set the provider's Invalidation flow to `default-provider-invalidation-flow` so every logout forces a full re-login.
2. Confirm plugin 0.1.3 under Jellyfin → Plugins and the Gitea runner registered.
3. Portal viewer during a real stream: corner chip `webrtc`; Windows Go Live with WarDogs audio (first Windows streamer).
@@ -86,7 +86,8 @@ Source of truth: this repo, `techy/Media-Server-Setup` on the stack's own Gitea
- Streams are not re-encoded for VFR/gaps by default (dropped frames are legitimate; a 3 h re-encode is hours of CPU); players hold the last frame across a gap. The timecode sidecar lists gaps. **Gap filling is available** (`FILL_GAPS_MS` in `normalize-sweep.service`, README "Timecode") — turn on when the box has a hardware encoder. Clips always get CFR.
- The sweep's VFR check used a gawk-only function; Debian's mawk made it pass everything silently. Portable now.
- A deploy block that is only half-run leaves old binaries in `/usr/local/bin`; `grep -c` the installed script before blaming logic.
- MediaMTX hot-reloads `paths`/recording settings but NOT `authMethod`/auth settings: those need a container restart (between streams).
- MediaMTX does not reload `mediamtx.yml` live here (bind-mounted file): any change to it needs `docker compose restart mediamtx` between streams. Nothing day-to-day touches that file.
- **Timecode verified live (2026-10-31)**: OBS + NVENC with the band → `source: strip`, drift −3 ms over 80 s, 0 gaps, 1920×1092 (band present); arrival time was 2.3 s late vs the first frame, which the strip corrected. Go Live from Linux Chromium (software encode) → strip decoded fine but 239 gaps and −85 ms: frame dropping, as expected on that path.
- MediaMTX stamps recording filenames in its container clock: without `TZ` it is UTC and the plugin reads names as Eastern (4 h off). `TZ=America/New_York` is set on the container.
- MediaMTX 1.21 refuses to start without `webrtcAdditionalHosts` or `webrtcIPsFromInterfaces`; configure.py writes the public host in.
- Jellyfin honours provider-supplied season numbers on a Shows library (day seasons appeared). Next check: Monday 04:10 ET should fold them into a week season.

View File

@@ -364,7 +364,8 @@ srtAddress: :8890
# ---- auth -------------------------------------------------------------
# Publishing is authorised by dumpd (/mtx-auth) with short-lived tokens minted for Discord-authenticated
# members of the streamers group. Reads are allowed; viewers are gated by Caddy + Authentik. No passwords.
# NOTE: MediaMTX hot-reloads paths/recording settings but NOT the auth section — restart after auth changes.
# NOTE: on this setup (bind-mounted file) MediaMTX does not reload this file live: restart after any change here.
# Day-to-day operations (streamers, tokens, titles) never touch this file.
authMethod: http
authHTTPAddress: http://{auth_gw}:8081/mtx-auth
authHTTPExclude:
@@ -475,8 +476,9 @@ def apply(domain, admin_pw, streamers, editors, auth_host="", watch_host="", dis
run(dc + ["up", "-d", "--build"]) # recreates only services whose compose definition changed
run(dc + ["restart", "copyparty"]) # copyparty re-reads its config on start
run(dc + ["exec", "caddy", "caddy", "reload", "--config", "/etc/caddy/Caddyfile"]) # zero-downtime
print(" mediamtx: hot-reloads most of mediamtx.yml, but NOT auth changes. If you changed auth settings, run")
print(" docker compose restart mediamtx between streams (cuts a live recording).")
print(" mediamtx: config changes are NOT picked up live on this setup (bind-mounted file). If mediamtx.yml changed,")
print(" run docker compose restart mediamtx between streams (cuts a live recording). Adding/removing")
print(" streamers or tokens never touches mediamtx.yml, so day-to-day nothing restarts.")
def show(domain, admin_pw, streamers, editors, auth_host="", watch_host="", git_host="", bot_host="", live_host=""):
print(f"\n== domain : {domain or '(not set)'}")