The Qt app was using PulseAudio's 'default.monitor' which captures ALL
system audio (YouTube Music, Spotify, browser, etc.). This caused listeners
to hear whatever was playing on the DJ's system, not just the DJ mix.
Added PulseAudioIsolator class that:
- Creates a virtual PulseAudio null sink ('techdj_stream')
- Routes only this app's audio to the virtual sink
- Creates a loopback so the DJ still hears their mix through speakers
- Captures from the virtual sink's monitor (only DJ audio)
- Reference-counted: shared between streaming and recording workers
- Automatically cleans up stale sinks from previous crashes
- Periodically re-routes audio to catch new tracks/streams
- Falls back to default.monitor if pactl is unavailable
Both StreamingWorker and RecordingWorker now use the isolator.
- script.js: Remove ~500 lines of dead listener mode code (initListenerMode, enableListenerAudio, setListenerVolume, startListenerVUMeter, getMp3FallbackUrl, listener variables). Listener page now uses listener.js exclusively.
- script.js: Remove ?listen=true detection from DOMContentLoaded that could activate broken listener UI on DJ panel.
- script.js: Clean up initSocket() to remove dead listener mode detection logic.
- index.html: Remove dead #listener-mode div (now served by listener.html).
- server.py: Move 'abort' import to top-level Flask import instead of per-request import.
- techdj_qt.py: Fix StreamingWorker to create fresh socketio.Client on each streaming session, preventing stale socket state on reconnect.
- techdj_qt.py: Fix time.sleep(0.2) blocking GUI thread in stop_streaming() by removing it and using try/except for clean disconnect.
- Add config.example.json with all options: host, dj_port, listener_port,
dj_panel_password, secret_key, music_folder, stream_bitrate, max_upload_mb,
cors_origins, debug (copy to config.json to use)
- server.py: drive host/ports/secrets/CORS/upload limit from config.json;
fix serve_static to use allowlist only; move re import to top-level;
fix inconsistent indentation in login/logout/before_request handlers
- script.js: fix undefined decks.crossfader in updateUIFromMixerStatus;
declare mediaRecorder as a proper let variable
- techdj_qt.py: replace blocking time.sleep poll in YTDownloadWorker with
non-blocking QTimer; fix fragile or-chained lambda in recording reset