fixxes for background playbacks
This commit is contained in:
+112
-19
@@ -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'])
|
||||
|
||||
Reference in New Issue
Block a user