fixxes for background playbacks

This commit is contained in:
2026-02-18 22:35:10 +01:00
parent f580d67ccb
commit 36d1a36549
3 changed files with 136 additions and 50 deletions
+112 -19
View File
@@ -15,6 +15,9 @@ from app.utils.helpers import load_default_sounds, load_soundboard_configs, load
logger = logging.getLogger(__name__)
audio_lock = threading.Lock()
auto_random_lock = threading.Lock()
auto_random_timer = None
auto_random_params = {}
# Sound-Cache: path -> pygame.mixer.Sound
loaded_sounds = {}
@@ -82,6 +85,55 @@ def _play_sound_entry(sound_entry, channel_req=None, loop_req=0):
return None # Erfolg
def _pick_random_sound():
if state.current_soundboard is None:
return None, "Kein Soundboard geladen"
rnd_list = state.current_soundboard.get('random_pool') or state.current_soundboard.get('sounds', [])
if not rnd_list:
return None, "Keine Random-Sounds definiert"
now = time.time()
candidates = []
for sound in rnd_list:
sid = sound.get('id') or sound.get('file')
dq = _prune_history(sid, now)
if len(dq) < MAX_PER_HOUR:
candidates.append(sound)
if not candidates:
return None, "Limit erreicht (2x pro Stunde)"
sound_entry = random.choice(candidates)
sid = sound_entry.get('id') or sound_entry.get('file')
random_history[sid].append(now)
sound_entry.setdefault('id', sid)
return sound_entry, None
def _auto_random_tick():
global auto_random_timer
with auto_random_lock:
if not state.auto_random_active:
return
imin = auto_random_params.get('imin', 5)
imax = auto_random_params.get('imax', 10)
# Play one random sound
sound_entry, err = _pick_random_sound()
if sound_entry:
_play_sound_entry(sound_entry, sound_entry.get('channel'), sound_entry.get('loop'))
# schedule next
delay = rand_between(imin, imax) * 60
with auto_random_lock:
if state.auto_random_active:
auto_random_timer = threading.Timer(delay, _auto_random_tick)
auto_random_timer.daemon = True
auto_random_timer.start()
def rand_between(a, b):
return a + random.random() * (b - a)
def _prune_history(sound_id, now):
dq = random_history[sound_id]
while dq and now - dq[0] > WINDOW_SECONDS:
@@ -331,6 +383,8 @@ def api_soundboard_load():
current_app.config['SOUNDBOARD_CONFIG_DIR'],
current_app.config['SOUNDS_DIR'])
state.current_soundboard = sb
# Stoppe ggf. laufende Auto-Randoms beim Laden neuer Themen
_stop_auto_random_internal()
logger.info(f"Soundboard geladen: {filename}")
return jsonify({"success": True, "soundboard": sb})
except Exception as e:
@@ -343,30 +397,69 @@ def api_soundboard_play_random():
if state.current_soundboard is None:
return jsonify({"success": False, "message": "Kein Soundboard geladen"}), 400
rnd_list = state.current_soundboard.get('random_pool') or state.current_soundboard.get('sounds', [])
if not rnd_list:
return jsonify({"success": False, "message": "Keine Random-Sounds definiert"}), 400
now = time.time()
candidates = []
for sound in rnd_list:
sid = sound.get('id') or sound.get('file')
dq = _prune_history(sid, now)
if len(dq) < MAX_PER_HOUR:
candidates.append(sound)
if not candidates:
return jsonify({"success": False, "message": "Limit erreicht (2x pro Stunde)"}), 429
sound_entry = random.choice(candidates)
sid = sound_entry.get('id') or sound_entry.get('file')
sound_entry, err = _pick_random_sound()
if err:
return jsonify({"success": False, "message": err}), 429 if "Limit" in err else 400
res = _play_sound_entry(sound_entry)
if res is not None:
return res # already Response
random_history[sid].append(now)
return jsonify({"success": True, "message": f"Random: {sound_entry.get('name', sid)}"})
return jsonify({"success": True, "message": f"Random: {sound_entry.get('name', sound_entry.get('id'))}"})
@api_bp.route('/soundboard/auto_start', methods=['POST'])
def api_soundboard_auto_start():
if state.current_soundboard is None:
return jsonify({"success": False, "message": "Kein Soundboard geladen"}), 400
data = request.get_json() or {}
imin = float(data.get('interval_min', 5))
imax = float(data.get('interval_max', 10))
dmin = float(data.get('delay_min', 3))
dmax = float(data.get('delay_max', 12))
if imax < imin: imax = imin
if dmax < dmin: dmax = dmin
delay = rand_between(dmin, dmax) * 60
with auto_random_lock:
_stop_auto_random_internal()
state.auto_random_active = True
auto_random_params['imin'] = imin
auto_random_params['imax'] = imax
auto_random_params['dmin'] = dmin
auto_random_params['dmax'] = dmax
global auto_random_timer
auto_random_timer = threading.Timer(delay, _auto_random_tick)
auto_random_timer.daemon = True
auto_random_timer.start()
logger.info(f"Auto-Random gestartet (delay {delay/60:.1f} min, intervall {imin}-{imax} min)")
return jsonify({"success": True, "message": "Auto-Random gestartet",
"next_seconds": delay})
@api_bp.route('/soundboard/auto_stop', methods=['POST'])
def api_soundboard_auto_stop():
stopped = _stop_auto_random_internal()
return jsonify({"success": True, "message": "Auto-Random gestoppt", "was_running": stopped})
@api_bp.route('/soundboard/status', methods=['GET'])
def api_soundboard_status():
with auto_random_lock:
active = state.auto_random_active
return jsonify({"active": active})
def _stop_auto_random_internal():
global auto_random_timer
stopped = False
with auto_random_lock:
if auto_random_timer:
auto_random_timer.cancel()
auto_random_timer = None
stopped = True
state.auto_random_active = False
return stopped
@api_bp.route('/stop_sound', methods=['POST'])
+1
View File
@@ -5,6 +5,7 @@ current_hub = None
current_module = None
current_device = None
current_soundboard = None # aktives Soundboard-Thema
auto_random_active = False
# Optional: Funktionen zum Setzen/Resetten (für Klarheit)
def reset_state():