diff --git a/backend/app/recorders/recorder.py b/backend/app/recorders/recorder.py index e5df2a6..e38f694 100644 --- a/backend/app/recorders/recorder.py +++ b/backend/app/recorders/recorder.py @@ -90,7 +90,7 @@ class PortRecorder: self._start_time = None def get_status(self) -> PortStatus: - is_recording = self._process is not None and not self._process.done() + is_recording = self._process is not None and self._process.returncode is None uptime_seconds = 0 if self._start_time is not None: diff --git a/docker-compose.truenas.yml b/docker-compose.truenas.yml new file mode 100644 index 0000000..7e9003f --- /dev/null +++ b/docker-compose.truenas.yml @@ -0,0 +1,47 @@ +version: '3.8' + +# TrueNAS (Wooglin) override — no physical Deltacast card. +# The backend auto-detects missing /dev/deltacast{n} and falls back to +# a lavfi testsrc2 signal (colour bars + clock + 1 kHz tone) per port. + +services: + backend: + build: + context: ./backend + dockerfile: Dockerfile + ports: + - "8000:8000" + # NOTE: no devices: block — lavfi fallback kicks in automatically + volumes: + - recordings:/recordings + - hls:/tmp/hls + environment: + - FFMPEG_PATH=/usr/bin/ffmpeg + - RECORDING_DIR=/recordings + - DELTACAST_PORT_COUNT=8 + - SRT_ENABLED=true + - SRT_LATENCY=5000 + - PORT=8000 + - LOG_LEVEL=INFO + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/api/health"] + interval: 30s + timeout: 10s + retries: 3 + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + ports: + - "8088:80" + depends_on: + - backend + restart: unless-stopped + +volumes: + recordings: + driver: local + hls: + driver: local diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 231885b..d141b74 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -29,7 +29,7 @@ export default function App() { const wsUrl = `ws://${window.location.host}/ws`; const { isConnected, lastMessage } = useWebSocket(wsUrl); - const { startRecording, stopRecording, injectSCTE35, injectSCTE35ForPort } = useRecorder(); + const { startRecording, stopRecording, injectSCTE35, injectSCTE35ForPort, error: recorderError } = useRecorder(); // Clock useEffect(() => { @@ -88,6 +88,17 @@ export default function App() { return (