mirror of
https://github.com/myronblair/ai-context
synced 2026-07-27 17:23:53 -05:00
59 lines
7.2 KiB
Markdown
59 lines
7.2 KiB
Markdown
# StreamHoard Reference Card (CT104, 10.48.200.104)
|
|
|
|
## What it is
|
|
Self-hosted Netflix-style media app — repo `myronblair/kino-app` (private), branded **"StreamHoard"** in the UI (internal names/container names still say `kino`, cosmetic only). FastAPI + React + MongoDB, Docker Compose in `/opt/kino` on CT104 (Debian 12 LXC on PVE1, 8 cores/12GB RAM/500GB disk).
|
|
|
|
Login: `admin@streamhoard.local` — password set via the app's own self-service "Change Password" (Settings page), not stored in this doc. App version shown in the navbar (`GET /api/version`, source of truth is `backend/VERSION` in the repo).
|
|
|
|
## Container stack
|
|
| Container | Role | Port (host) |
|
|
|---|---|---|
|
|
| `streamhoard-frontend` | React UI, reverse-proxies `/api` to backend | 8080 |
|
|
| `streamhoard-backend` | FastAPI — auth, catalog, streaming, transcode queue, library scan/cleanup | internal 8001 |
|
|
| `streamhoard-mongo` | MongoDB **pinned to 4.4** (PVE1's CPU lacks AVX, mongo:7+ SIGILL-crashes without this pin) | internal |
|
|
| `streamhoard-gluetun` | VPN gateway (NordVPN/WireGuard), real kill switch — `iptables` default policy `DROP`, only `tun0`/LAN/VPN-handshake traffic allowed out | proxies 7878/8989/9696/8082 |
|
|
| `streamhoard-radarr` / `-sonarr` / `-prowlarr` / `-qbittorrent` | `network_mode: service:gluetun` — share gluetun's netns, so kill switch covers all their traffic, not just torrent | via gluetun |
|
|
| `streamhoard-portainer` | Docker management GUI (`https://10.48.200.104:9443`) | 9443 |
|
|
|
|
## Media/music mounts
|
|
- NAS (`10.48.200.249`) mounts happen on **PVE1 itself**, then pass into CT104 as LXC mountpoints (`/etc/pve/lxc/104.conf`) — unprivileged LXCs can't do NFS/CIFS client mounts directly.
|
|
- `mp0`: NFS `/volume1/video` → `/mnt/nas-video` (PVE1) → `/mnt/nas/video` (CT104). Radarr/Sonarr root folders `/mnt/nas/video/movies`, `/mnt/nas/video/tv`.
|
|
- `mp1`: CIFS `Storage` share → `/mnt/nas-backups` — MSP360 backup destination.
|
|
- `mp2`: CIFS `music` share (no NFS export exists for it) → `/mnt/nas-music` (PVE1, read-only) → `/mnt/nas/music` (CT104, read-only). Real content is under `Music/<Artist>/<Album>/<Track>`; sibling folders (`Apple Music`, `Automatically Add to Apple Music`, `Downloads-Music`) are Music.app-internal and deliberately not scanned — may contain DRM'd Apple Music subscription tracks, which no third-party player can decrypt/play.
|
|
- Backend container bind-mounts `/mnt/nas/video` and `/mnt/nas/music` read-only at their identical absolute paths, so Radarr/Sonarr/Library-Scan-reported file paths resolve with zero translation.
|
|
|
|
## Library Scan (admin, separate from Radarr/Sonarr)
|
|
Walks the NAS mounts directly for movies/TV/music not already in the library — `storage_type="scan"` on the resulting Movie/Episode/Track record, fully integrated (streamable, transcodable, listed in Admin) same as Radarr/Sonarr imports. Select All / Deselect All per tab. Movie/TV scans are fast (filesystem stat only); music scan defers tag reads to import time (reading ID3/MP4 tags during the scan itself was the original bottleneck).
|
|
|
|
## Music library
|
|
`Track` model (title/artist/album/track_number/duration/album_art_url), streamed via ranged `/api/stream/track/{id}`. Persistent bottom player (queue, seek, output-device picker via the Audio Output Devices API for already-paired Bluetooth speakers — Chrome/Edge only, browsers can't pair new BT devices). Album art: MusicBrainz + Cover Art Archive lookup (free, no key, 1 req/sec rate limit respected), triggered from Library Scan's Music tab; falls back to a branded placeholder (not a bare icon) until art is set. NAS mount is read-only, so Apple Music/iTunes can keep managing the same files independently.
|
|
|
|
## Library Cleanup (admin)
|
|
Background job (`POST .../scan/start`, `GET .../status`, `POST .../stop` — has a real Stop button, not just a spinner) checking for: missing files, orphaned episodes/HLS output, stuck transcode jobs (running 2h+ with no completion), duplicate imports, orphaned subtitles, dangling watchlist/progress rows. Nothing deletes until you click Fix. File-existence checks run in a small thread pool off the event loop — an earlier version did this synchronously and froze the *entire server* for as long as the scan took (a plain `GET /api/movies` would hang); confirmed fixed, don't reintroduce blocking filesystem calls in an async endpoint here.
|
|
|
|
## Transcode queue
|
|
One job at a time, `nice -n 10` (was 19), threads divided across however many ABR variants a job has so the total stays near a fixed budget instead of each variant claiming the full count independently. Auto-transcode setting (`off`/`quick`/`abr`, in Settings) auto-queues new imports.
|
|
|
|
Three quality modes:
|
|
- **quick** — stream-copy both streams (fast, no quality loss, but doesn't fix incompatible audio).
|
|
- **abr** — full re-encode to multi-bitrate H.264/AAC HLS (slow — 4 variants, only way to fix HEVC/x265 video).
|
|
- **audio** — video stream-copied untouched, only audio re-encoded to AAC (fast — no video re-encoding). Fixes the single most common playback problem: BDRip/AVI sources very often carry AC3/DTS/EAC3 audio, which no browser can decode natively — browsers respond by hiding the volume control entirely, not by erroring. `POST /transcode/queue/fix-audio-all` cancels the pending backlog and replaces it with the fast audio-only fix library-wide; run a real ABR pass later for actual multi-bitrate quality once there's no urgency.
|
|
|
|
Retry (`/transcode/queue/{id}/retry`) and bulk retry-failed never delete the movie/episode — only the job history and `hls_status` flag change.
|
|
|
|
## Requests → Radarr/Sonarr
|
|
Pending user requests can be Approved (Movie or TV) by an admin: looks up the title via Radarr/Sonarr's own lookup API, adds the best match with search-on-add enabled. Atomically claims the request (`pending → approving → approved`) so a double-click or two admins can't both add the same content; releases the claim back to `pending` on failure so it can be retried. Live status line (searching/queued/downloading/downloaded) reads from Radarr/Sonarr's own queue, polled every 15s — real data relayed through Radarr's own refresh cycle, not a direct qBittorrent connection.
|
|
|
|
## MSP360 backup
|
|
Weekly full / daily incremental to the NAS (`/mnt/nas-backups/MSPBackups`, plan **StreamHoard-Backup**). Installed via `dpkg-repack` from the NovaCPX install (no public Linux .deb download exists). Excludes the media/music NAS mounts, the backup destination itself, Docker image layers, and the regenerable HLS transcode cache.
|
|
|
|
## JARVIS agent
|
|
Registered as `streamhoard104`, `watch_services: ["docker"]` — watches the Docker daemon only, not individual containers. A `/usr/local/bin/streamhoard-watchdog.sh` cron (every 5 min) separately restarts any StreamHoard container that isn't running, since Docker's own `restart: unless-stopped` policy doesn't always trigger reliably.
|
|
|
|
## Copyright
|
|
Repo has a `NOTICE` file ("Property of TomTom Enterprises."). Commits to this repo omit AI-authorship trailers.
|
|
|
|
## Open items
|
|
- No reverse proxy/TLS in front of StreamHoard, no LAN-only firewall rule.
|
|
- Large transcode backlog from initial library import — being worked through via the fast audio-fix pass; a full ABR pass for real adaptive-bitrate quality is still pending for most of the library.
|