# gunicorn.conf.py — Gunicorn configuration for TechDJ # Used by production.py. Do not run gunicorn directly without this file. # # Architecture: # - Gunicorn (eventlet worker, 1 worker) serves the DJ panel app on DJ_PORT. # - post_worker_init spawns the listener app on LISTENER_PORT and starts # background greenlets — all within the same worker process so they share # in-process state (SRT state, pre-roll buffer, queue, etc.). import os import json # ── Load ports from config.json ────────────────────────────────────────────── def _load_cfg(): try: with open(os.path.join(os.path.dirname(__file__), 'config.json')) as f: return json.load(f) except Exception: return {} _cfg = _load_cfg() # ── Gunicorn settings ───────────────────────────────────────────────────────── worker_class = 'eventlet' workers = 1 # Must be 1 — eventlet handles concurrency via greenlets # and both apps share in-process state. bind = f"{_cfg.get('host', '0.0.0.0')}:{_cfg.get('dj_port', 5000)}" timeout = 0 # Disable worker timeout — long-lived SSE/WS connections. keepalive = 5 # Log format accesslog = '-' # stdout — production.py redirects to techdj.log errorlog = '-' loglevel = 'info' # ── Hooks ───────────────────────────────────────────────────────────────────── def post_worker_init(worker): """Called after the eventlet worker is fully initialised. Starts the listener server + background greenlets in the same process as the DJ panel so they share all in-process state. """ from server import _start_background_tasks _start_background_tasks()