forked from computertech/techdj
Add /stream_debug and improve MP3 transcoder startup
This commit is contained in:
31
server.py
31
server.py
@@ -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))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user