added new functions for random sounds
This commit is contained in:
parent
4a4f55fbe9
commit
63e62670d5
@ -24,8 +24,8 @@ AUTO_STATE_PATH = os.path.join(Config.LOG_DIR, 'auto_random_state.json')
|
||||
# Sound-Cache: path -> pygame.mixer.Sound
|
||||
loaded_sounds = {}
|
||||
random_history = defaultdict(deque) # sound_id -> deque[timestamps]
|
||||
MAX_PER_HOUR = 2
|
||||
WINDOW_SECONDS = 3600
|
||||
DEFAULT_MAX_PER_HOUR = 2
|
||||
DEFAULT_WINDOW_SECONDS = 3600
|
||||
|
||||
def _ensure_mixer():
|
||||
from config import Config
|
||||
@ -103,8 +103,9 @@ def _pick_random_sound():
|
||||
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:
|
||||
max_per_hour, window_seconds = _get_limits(sound)
|
||||
dq = _prune_history(sid, now, window_seconds)
|
||||
if len(dq) < max_per_hour:
|
||||
candidates.append(sound)
|
||||
|
||||
if not candidates:
|
||||
@ -117,6 +118,30 @@ def _pick_random_sound():
|
||||
return sound_entry, None
|
||||
|
||||
|
||||
def _get_limits(sound_entry=None):
|
||||
"""
|
||||
Ermittelt Grenzen für Random-Wiedergabe.
|
||||
Priorität: pro Sound (max_per_hour, window_minutes) -> aktuelles Soundboard -> Defaults.
|
||||
"""
|
||||
max_per_hour = DEFAULT_MAX_PER_HOUR
|
||||
window_seconds = DEFAULT_WINDOW_SECONDS
|
||||
|
||||
if state.current_soundboard:
|
||||
sb = state.current_soundboard
|
||||
max_per_hour = int(sb.get('random_limit_per_hour', max_per_hour))
|
||||
window_minutes = sb.get('random_window_minutes')
|
||||
if window_minutes:
|
||||
window_seconds = int(window_minutes * 60)
|
||||
|
||||
if sound_entry:
|
||||
if 'max_per_hour' in sound_entry:
|
||||
max_per_hour = int(sound_entry['max_per_hour'])
|
||||
if 'window_minutes' in sound_entry:
|
||||
window_seconds = int(sound_entry['window_minutes'] * 60)
|
||||
|
||||
return max_per_hour, window_seconds
|
||||
|
||||
|
||||
def _auto_random_tick():
|
||||
global auto_random_timer
|
||||
with auto_random_lock:
|
||||
@ -165,9 +190,9 @@ def _persist_auto_state(clear=False):
|
||||
logger.error(f"Persist auto-random state fehlgeschlagen: {e}")
|
||||
|
||||
|
||||
def _prune_history(sound_id, now):
|
||||
def _prune_history(sound_id, now, window_seconds=DEFAULT_WINDOW_SECONDS):
|
||||
dq = random_history[sound_id]
|
||||
while dq and now - dq[0] > WINDOW_SECONDS:
|
||||
while dq and now - dq[0] > window_seconds:
|
||||
dq.popleft()
|
||||
return dq
|
||||
|
||||
@ -451,6 +476,8 @@ def api_soundboard_auto_start():
|
||||
imax = float(data.get('interval_max', 10))
|
||||
dmin = float(data.get('delay_min', 3))
|
||||
dmax = float(data.get('delay_max', 12))
|
||||
limit_ph = int(data.get('limit_per_hour', DEFAULT_MAX_PER_HOUR))
|
||||
window_min = float(data.get('window_minutes', DEFAULT_WINDOW_SECONDS/60))
|
||||
if imax < imin: imax = imin
|
||||
if dmax < dmin: dmax = dmin
|
||||
|
||||
@ -465,6 +492,8 @@ def api_soundboard_auto_start():
|
||||
auto_random_params['dmax'] = dmax
|
||||
auto_random_params['next_ts'] = time.time() + delay
|
||||
auto_random_params['theme'] = state.current_soundboard.get('filename')
|
||||
auto_random_params['limit_per_hour'] = limit_ph
|
||||
auto_random_params['window_seconds'] = window_min * 60
|
||||
global auto_random_timer
|
||||
auto_random_timer = threading.Timer(delay, _auto_random_tick)
|
||||
auto_random_timer.daemon = True
|
||||
@ -500,6 +529,8 @@ def api_soundboard_status():
|
||||
dmin = auto_random_params.get('dmin')
|
||||
dmax = auto_random_params.get('dmax')
|
||||
current_theme = state.current_soundboard.get('filename') if state.current_soundboard else None
|
||||
limit_ph = auto_random_params.get('limit_per_hour', DEFAULT_MAX_PER_HOUR)
|
||||
window_sec = auto_random_params.get('window_seconds', DEFAULT_WINDOW_SECONDS)
|
||||
next_seconds = max(0, next_ts - time.time()) if next_ts else None
|
||||
return jsonify({
|
||||
"active": active,
|
||||
@ -508,7 +539,9 @@ def api_soundboard_status():
|
||||
"interval_max": imax,
|
||||
"delay_min": dmin,
|
||||
"delay_max": dmax,
|
||||
"current_theme": current_theme
|
||||
"current_theme": current_theme,
|
||||
"limit_per_hour": limit_ph,
|
||||
"window_minutes": window_sec/60 if window_sec else None
|
||||
})
|
||||
|
||||
|
||||
@ -557,7 +590,9 @@ def _restore_auto_state():
|
||||
'dmin': data.get('dmin', 3),
|
||||
'dmax': data.get('dmax', 12),
|
||||
'next_ts': next_ts,
|
||||
'theme': theme
|
||||
'theme': theme,
|
||||
'limit_per_hour': data.get('limit_per_hour', DEFAULT_MAX_PER_HOUR),
|
||||
'window_seconds': data.get('window_seconds', DEFAULT_WINDOW_SECONDS)
|
||||
})
|
||||
delay = max(1, next_ts - time.time())
|
||||
global auto_random_timer
|
||||
|
||||
@ -144,6 +144,14 @@
|
||||
<label class="form-label">Startverzögerung max (Minuten)</label>
|
||||
<input id="auto-delay-max" type="number" class="form-control" value="12" min="0" max="120">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Max Wiederholungen pro Stunde</label>
|
||||
<input id="auto-limit-ph" type="number" class="form-control" value="2" min="1" max="50">
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Limit-Fenster (Minuten)</label>
|
||||
<input id="auto-window-min" type="number" class="form-control" value="60" min="1" max="240">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 d-flex gap-2 align-items-center">
|
||||
<button id="auto-start" class="btn btn-success" ${!randBtn.disabled ? '' : 'disabled'}>Auto-Start</button>
|
||||
@ -161,11 +169,17 @@
|
||||
const imax = Math.max(imin, parseInt(document.getElementById('auto-interval-max').value, 10) || 10);
|
||||
const dmin = Math.max(0, parseInt(document.getElementById('auto-delay-min').value, 10) || 3);
|
||||
const dmax = Math.max(dmin, parseInt(document.getElementById('auto-delay-max').value, 10) || 12);
|
||||
const limitPh = Math.max(1, parseInt(document.getElementById('auto-limit-ph').value, 10) || 2);
|
||||
const windowMin = Math.max(1, parseInt(document.getElementById('auto-window-min').value, 10) || 60);
|
||||
|
||||
const res = await fetch('/api/soundboard/auto_start', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ interval_min: imin, interval_max: imax, delay_min: dmin, delay_max: dmax })
|
||||
body: JSON.stringify({
|
||||
interval_min: imin, interval_max: imax,
|
||||
delay_min: dmin, delay_max: dmax,
|
||||
limit_per_hour: limitPh, window_minutes: windowMin
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
@ -212,6 +226,8 @@
|
||||
if (data.interval_max) document.getElementById('auto-interval-max').value = data.interval_max;
|
||||
if (data.delay_min !== undefined && data.delay_min !== null) document.getElementById('auto-delay-min').value = data.delay_min;
|
||||
if (data.delay_max !== undefined && data.delay_max !== null) document.getElementById('auto-delay-max').value = data.delay_max;
|
||||
if (data.limit_per_hour !== undefined && data.limit_per_hour !== null) document.getElementById('auto-limit-ph').value = data.limit_per_hour;
|
||||
if (data.window_minutes !== undefined && data.window_minutes !== null) document.getElementById('auto-window-min').value = data.window_minutes;
|
||||
} else {
|
||||
status.textContent = 'Aus';
|
||||
start.disabled = false;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user