Fix buffering issue and app bugs: improve stream delivery, fixed reset UI desync, and EQ naming

This commit is contained in:
ComputerTech 2026-01-20 18:14:50 +00:00
parent 1776f631ef
commit 02f72e2372
3 changed files with 45 additions and 15 deletions

View File

@ -2242,7 +2242,17 @@ function initListenerMode() {
socket.on('broadcast_started', () => { socket.on('broadcast_started', () => {
const nowPlayingEl = document.getElementById('listener-now-playing'); const nowPlayingEl = document.getElementById('listener-now-playing');
if (nowPlayingEl) nowPlayingEl.textContent = '🎵 Stream is live!'; if (nowPlayingEl) nowPlayingEl.textContent = '🎵 Stream is live!';
// Reset MediaSource for fresh stream if needed
// Force a reload of the audio element to capture the fresh stream
if (window.listenerAudio) {
console.log('🔄 Broadcast started: Refreshing audio stream...');
const wasPlaying = !window.listenerAudio.paused;
window.listenerAudio.src = getMp3FallbackUrl();
window.listenerAudio.load();
if (wasPlaying || window.listenerAudioEnabled) {
window.listenerAudio.play().catch(e => console.warn('Auto-play after refresh blocked:', e));
}
}
}); });
socket.on('stream_status', (data) => { socket.on('stream_status', (data) => {

View File

@ -55,7 +55,7 @@ _transcode_threads_started = False
_transcoder_bytes_out = 0 _transcoder_bytes_out = 0
_transcoder_last_error = None _transcoder_last_error = None
_last_audio_chunk_ts = 0.0 _last_audio_chunk_ts = 0.0
_mp3_preroll = collections.deque(maxlen=60) # Pre-roll (~2.5s at 192k) _mp3_preroll = collections.deque(maxlen=256) # Pre-roll (~10s at 192k with 1KB chunks)
def _start_transcoder_if_needed(is_mp3_input=False): def _start_transcoder_if_needed(is_mp3_input=False):
@ -141,6 +141,7 @@ def _start_transcoder_if_needed(is_mp3_input=False):
break break
continue continue
except Exception as e: except Exception as e:
if _ffmpeg_proc is not None:
print(f"⚠️ Transcoder writer error: {e}") print(f"⚠️ Transcoder writer error: {e}")
_transcoder_last_error = f'stdin write failed: {e}' _transcoder_last_error = f'stdin write failed: {e}'
break break
@ -153,8 +154,9 @@ def _start_transcoder_if_needed(is_mp3_input=False):
proc = _ffmpeg_proc proc = _ffmpeg_proc
while proc and proc.poll() is None: while proc and proc.poll() is None:
try: try:
# Use a larger read for efficiency # Smaller read for smoother delivery (1KB)
data = proc.stdout.read(4096) # This prevents buffering delays at lower bitrates
data = proc.stdout.read(1024)
if not data: if not data:
break break
_transcoder_bytes_out += len(data) _transcoder_bytes_out += len(data)
@ -199,6 +201,17 @@ def _stop_transcoder():
if proc is None: if proc is None:
return return
# Signal all listening clients to finish their stream
with _mp3_lock:
clients = list(_mp3_clients)
for q in clients:
try:
q.put_nowait(None)
except:
pass
_mp3_clients.clear()
try: try:
proc.terminate() proc.terminate()
except Exception: except Exception:

View File

@ -57,6 +57,7 @@ class AudioEngine:
'loop_active': False, 'loop_active': False,
'repeat': False, 'repeat': False,
'queue': [], 'queue': [],
'needs_next_track': False,
}, },
'B': { 'B': {
'audio_data': None, 'audio_data': None,
@ -75,6 +76,7 @@ class AudioEngine:
'loop_active': False, 'loop_active': False,
'repeat': False, 'repeat': False,
'queue': [], 'queue': [],
'needs_next_track': False,
} }
} }
@ -410,8 +412,8 @@ class BroadcastThread(QThread):
# Thread to read encoded chunks from stdout # Thread to read encoded chunks from stdout
def read_output(): def read_output():
# Smaller buffer for more frequent updates (4KB = ~0.15s @ 192k) # Smaller buffer for more frequent updates (2KB = ~0.08s @ 192k)
buffer_size = 4096 buffer_size = 2048
while self.running: while self.running:
try: try:
data = self.process.stdout.read(buffer_size) data = self.process.stdout.read(buffer_size)
@ -806,8 +808,9 @@ class DeckWidget(QWidget):
eq_widget = QWidget() eq_widget = QWidget()
eq_layout = QHBoxLayout(eq_widget) eq_layout = QHBoxLayout(eq_widget)
eq_layout.setSpacing(8) eq_layout.setSpacing(8)
self.eq_sliders = {}
for band in ['HI', 'MID', 'LO']: for band in ['HIGH', 'MID', 'LOW']:
band_widget = QWidget() band_widget = QWidget()
band_layout = QVBoxLayout(band_widget) band_layout = QVBoxLayout(band_widget)
band_layout.setSpacing(2) band_layout.setSpacing(2)
@ -819,6 +822,7 @@ class DeckWidget(QWidget):
slider.setFixedHeight(80) slider.setFixedHeight(80)
slider.setStyleSheet(self.get_slider_style()) slider.setStyleSheet(self.get_slider_style())
slider.valueChanged.connect(lambda v, b=band.lower(): self.on_eq_change(b, v)) slider.valueChanged.connect(lambda v, b=band.lower(): self.on_eq_change(b, v))
self.eq_sliders[band.lower()] = slider
label = QLabel(band) label = QLabel(band)
label.setStyleSheet("color: #888; font-size: 9px;") label.setStyleSheet("color: #888; font-size: 9px;")
@ -1001,20 +1005,23 @@ class DeckWidget(QWidget):
def reset_deck(self): def reset_deck(self):
"""Reset all deck controls to default values""" """Reset all deck controls to default values"""
# Setting values on sliders will trigger the valueChanged signal
# which will in turn update the audio engine.
# Reset volume to 80% # Reset volume to 80%
self.volume_slider.setValue(80) self.volume_slider.setValue(80)
# Reset speed to 100% # Reset speed to 100%
self.speed_slider.setValue(100) self.speed_slider.setValue(100)
# Reset EQ to 0 (just update the engine, sliders will update via signals) # Reset EQ sliders to 0
self.audio_engine.set_eq(self.deck_id, 'high', 0) if hasattr(self, 'eq_sliders'):
self.audio_engine.set_eq(self.deck_id, 'mid', 0) for band, slider in self.eq_sliders.items():
self.audio_engine.set_eq(self.deck_id, 'low', 0) slider.setValue(0)
# Reset filters # Reset filter sliders
self.audio_engine.set_filter(self.deck_id, 'lowpass', 100) self.lp_slider.setValue(100)
self.audio_engine.set_filter(self.deck_id, 'highpass', 0) self.hp_slider.setValue(0)
print(f"🔄 Deck {self.deck_id} reset to defaults") print(f"🔄 Deck {self.deck_id} reset to defaults")