Add /stream_debug and improve MP3 transcoder startup

This commit is contained in:
3nd3r
2026-01-02 22:27:04 -06:00
parent 8cf76c1d71
commit 249b1cb210

View File

@@ -29,6 +29,9 @@ _ffmpeg_in_q = queue.Queue(maxsize=200)
_mp3_clients = set() # set[queue.Queue] _mp3_clients = set() # set[queue.Queue]
_mp3_lock = threading.Lock() _mp3_lock = threading.Lock()
_transcode_threads_started = False _transcode_threads_started = False
_transcoder_bytes_out = 0
_transcoder_last_error = None
_last_audio_chunk_ts = 0.0
def _start_transcoder_if_needed(): def _start_transcoder_if_needed():
@@ -62,7 +65,10 @@ def _start_transcoder_if_needed():
print('⚠️ ffmpeg not found; /stream.mp3 fallback disabled') print('⚠️ ffmpeg not found; /stream.mp3 fallback disabled')
return return
print('🎛️ ffmpeg transcoder started for /stream.mp3')
def _writer(): def _writer():
global _transcoder_last_error
while True: while True:
chunk = _ffmpeg_in_q.get() chunk = _ffmpeg_in_q.get()
if chunk is None: if chunk is None:
@@ -74,9 +80,11 @@ def _start_transcoder_if_needed():
proc.stdin.write(chunk) proc.stdin.write(chunk)
except Exception: except Exception:
# If ffmpeg dies or pipe breaks, just stop writing. # If ffmpeg dies or pipe breaks, just stop writing.
_transcoder_last_error = 'stdin write failed'
break break
def _reader(): def _reader():
global _transcoder_bytes_out, _transcoder_last_error
proc = _ffmpeg_proc proc = _ffmpeg_proc
if proc is None or proc.stdout is None: if proc is None or proc.stdout is None:
return return
@@ -84,9 +92,11 @@ def _start_transcoder_if_needed():
try: try:
data = proc.stdout.read(4096) data = proc.stdout.read(4096)
except Exception: except Exception:
_transcoder_last_error = 'stdout read failed'
break break
if not data: if not data:
break break
_transcoder_bytes_out += len(data)
with _mp3_lock: with _mp3_lock:
clients = list(_mp3_clients) clients = list(_mp3_clients)
for q in clients: for q in clients:
@@ -120,8 +130,10 @@ def _stop_transcoder():
def _feed_transcoder(data: bytes): def _feed_transcoder(data: bytes):
global _last_audio_chunk_ts
if _ffmpeg_proc is None or _ffmpeg_proc.poll() is not None: if _ffmpeg_proc is None or _ffmpeg_proc.poll() is not None:
return return
_last_audio_chunk_ts = eventlet.greenthread.time.time()
try: try:
_ffmpeg_in_q.put_nowait(data) _ffmpeg_in_q.put_nowait(data)
except Exception: except Exception:
@@ -280,6 +292,21 @@ def setup_shared_routes(app):
}, },
) )
@app.route('/stream_debug')
def stream_debug():
proc = _ffmpeg_proc
running = proc is not None and proc.poll() is None
return jsonify({
'broadcast_active': broadcast_state.get('active', False),
'broadcast_mimeType': broadcast_state.get('mimeType'),
'ffmpeg_running': running,
'ffmpeg_found': (proc is not None),
'mp3_clients': len(_mp3_clients),
'transcoder_bytes_out': _transcoder_bytes_out,
'transcoder_last_error': _transcoder_last_error,
'last_audio_chunk_ts': _last_audio_chunk_ts,
})
# === DJ SERVER (Port 5000) === # === DJ SERVER (Port 5000) ===
dj_app = Flask(__name__, static_folder='.', static_url_path='') dj_app = Flask(__name__, static_folder='.', static_url_path='')
dj_app.config['SECRET_KEY'] = 'dj_panel_secret' dj_app.config['SECRET_KEY'] = 'dj_panel_secret'
@@ -344,6 +371,10 @@ def dj_audio(data):
# Relay audio chunk to all listeners immediately # Relay audio chunk to all listeners immediately
if broadcast_state['active']: if broadcast_state['active']:
listener_socketio.emit('audio_data', data, namespace='/') listener_socketio.emit('audio_data', data, namespace='/')
# Ensure MP3 fallback transcoder is running (if ffmpeg is installed)
if _ffmpeg_proc is None or _ffmpeg_proc.poll() is not None:
_start_transcoder_if_needed()
if isinstance(data, (bytes, bytearray)): if isinstance(data, (bytes, bytearray)):
_feed_transcoder(bytes(data)) _feed_transcoder(bytes(data))