Replace PulseAudio monitor capture with direct file streaming.
Qt6's FFmpeg/PipeWire-native audio backend ignores PULSE_SINK so
the monitor captured silence. StreamingWorker now receives the
playing file path via a command queue and pipes it through ffmpeg
-re (real-time rate) directly to the server as audio_chunk events.
- Add switch_file() / stop_file() to StreamingWorker
- Replace ffmpeg pulse capture loop with file-based cmd loop
- DeckWidget.play() calls switch_file(path, position_ms)
- DeckWidget.pause()/stop() calls stop_file()
- Add now_playing socket relay in server.py
- listener.js handles now_playing event to show track title
- Add deck_glow emission from Qt deck play/pause/stop
- server.py: Add _distribute_mp3() to route MP3 chunks directly to listener
queues without a second ffmpeg passthrough (halves pipeline latency, removes
the eventlet/subprocess blocking read that caused the Qt client to fail)
- server.py: dj_start no longer starts ffmpeg for is_mp3_input=True
- server.py: dj_audio routes to _distribute_mp3 vs _feed_transcoder based on format
- server.py: _transcoder_watchdog skips MP3-direct mode
- server.py: stream_mp3 endpoint no longer waits for ffmpeg proc when MP3 direct
- techdj_qt.py: Add -fflags nobuffer + -flush_packets 1 to reduce source latency
- techdj_qt.py: bufsize=0 and read(4096) instead of read(8192) for ~260ms chunks
- listener.js: Reduce broadcast_started connect delay 800ms -> 300ms
- server.py: Increase ping_timeout 10->60, ping_interval 5->25 to prevent
frequent socket disconnects during audio streaming
- server.py: Guard dj_start() against reconnect loops - only clear pre-roll
and emit broadcast_started on a fresh broadcast, not on DJ reconnects
- listener.js: Add polling fallback to transports (websocket-only caused
silent failure on upgrade error), set reconnectionAttempts to Infinity
- script.js: Same transport fallback fix for DJ panel socket
- techdj_qt.py: Add _broadcast_started flag to StreamingWorker.on_connect
so streaming_started signal only fires once; reconnects resume silently.
Reset flag in stop_streaming() for clean next session.
- Created standalone listener.html, listener.js, listener.css
- Listener server now serves listener.html instead of index.html
- Eliminates flash of DJ panel when loading listener page
- setup_shared_routes() accepts index_file parameter