Wiederholungsregel für die Grund-Playlist

Ein Playlist-Titel kommt erst wieder, wenn seit seinem letzten Einsatz
mindestens X % der Playlist gelaufen sind (Admin → Einstellungen → Ablauf,
Standard 50 %). Der Player merkt sich die gespielten Titel über Neustarts.
Wünsche sind ausgenommen (dort gilt weiter die Sperrzeit in Minuten).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 19:27:15 +01:00
co-authored by Claude Opus 5.5
parent 3610de7bae
commit df5567e6a3
10 changed files with 187 additions and 25 deletions
+1
View File
@@ -49,6 +49,7 @@ AENDERBAR = {
"wish_limit_count": (int, 1, 50),
"wish_limit_minutes": (int, 1, 1440),
"repeat_block_minutes": (int, 0, 1440),
"playlist_repeat_percent": (int, 0, 100),
"moderation": (bool, None, None),
"wishes_paused": (bool, None, None),
"block_explicit": (bool, None, None),
+2 -1
View File
@@ -105,7 +105,8 @@ STANDARD_EINSTELLUNGEN = {
"wishes_paused": False, # Wunschannahme pausiert
"wish_limit_count": 2, # max. Wünsche pro Gast …
"wish_limit_minutes": 30, # … pro Zeitraum
"repeat_block_minutes": 60, # Sperrzeit für Wiederholungen
"repeat_block_minutes": 60, # Sperrzeit für Wiederholungen (Wünsche)
"playlist_repeat_percent": 50, # Playlist-Titel erst wieder nach X % der Playlist
"block_explicit": False, # explizite Titel nicht erlauben
"access_enabled": False, # Gastzugang per Einladungslink/QR-Code (anfangs aus)
"access_token": "", # geheimer Teil des Einladungslinks /z/<token>
+1
View File
@@ -90,6 +90,7 @@ def pi_config(db: sqlite3.Connection = Depends(get_db)) -> dict:
"wish_every_n": e["wish_every_n"],
"playlist_id": e["playlist_id"],
"reshuffle_token": e["reshuffle_token"],
"playlist_repeat_percent": e["playlist_repeat_percent"],
"stopped": e["emergency_stop"],
"commands": offene_befehle(db),
}
+4 -2
View File
@@ -138,8 +138,10 @@
<div class="karte">
<h2>Ablauf</h2>
<div class="feld-zeile"><span>Jeder wievielte Titel ist ein Wunsch (N)</span><input id="e-n" type="number" min="1" max="20"></div>
<div class="feld-zeile"><span>Sperrzeit für Wiederholungen (Min.)</span><input id="e-sperrzeit" type="number" min="0" max="1440"></div>
<button class="gross akzent voll" type="button" data-speichern="e-n:wish_every_n,e-sperrzeit:repeat_block_minutes">Speichern</button>
<div class="feld-zeile"><span>Wunsch erst wieder erlaubt nach (Min.)</span><input id="e-sperrzeit" type="number" min="0" max="1440"></div>
<div class="feld-zeile"><span>Playlist-Titel erst wieder nach (% der Playlist)</span><input id="e-playlist-prozent" type="number" min="0" max="100"></div>
<p class="hinweis klein">Ein Titel der Grund-Playlist kommt erst wieder, wenn seit seinem letzten Einsatz mindestens so viel Prozent der Playlist gelaufen sind. 100 % = erst wenn alle anderen dran waren. Für Wünsche gilt nur die Minuten-Sperre.</p>
<button class="gross akzent voll" type="button" data-speichern="e-n:wish_every_n,e-sperrzeit:repeat_block_minutes,e-playlist-prozent:playlist_repeat_percent">Speichern</button>
</div>
<div class="karte">
<h2>Lautsprecher</h2>
+1
View File
@@ -265,6 +265,7 @@ function zeigeEinstellungen(z) {
const e = z.settings;
setzeWert("e-n", e.wish_every_n);
setzeWert("e-sperrzeit", e.repeat_block_minutes);
setzeWert("e-playlist-prozent", e.playlist_repeat_percent);
setzeWert("e-limit-anzahl", e.wish_limit_count);
setzeWert("e-limit-minuten", e.wish_limit_minutes);
setzeSchalter("s-explizit", e.block_explicit);
+3 -1
View File
@@ -176,8 +176,10 @@ def test_einstellungen_validiert(client):
h = anmelden(client)
assert client.post("/admin/api/settings", json={"wish_every_n": 4, "moderation": True}, headers=h).status_code == 200
assert (einstellung("wish_every_n"), einstellung("moderation")) == (4, True)
assert client.post("/admin/api/settings", json={"playlist_repeat_percent": 80}, headers=h).status_code == 200
assert einstellung("playlist_repeat_percent") == 80
for falsch in ({"wish_every_n": 0}, {"wish_every_n": "4"}, {"moderation": 1}, {"access_token": "x"},
{"emergency_stop": True}):
{"emergency_stop": True}, {"playlist_repeat_percent": 101}, {"playlist_repeat_percent": -1}):
assert client.post("/admin/api/settings", json=falsch, headers=h).status_code == 422, falsch
+1 -1
View File
@@ -42,7 +42,7 @@ def test_token_pflicht(client):
def test_config_standardwerte(client):
daten = client.get("/api/player/config", headers=AUTH).json()
assert daten == {"wish_every_n": 3, "playlist_id": "startliste", "reshuffle_token": 0,
"stopped": False, "commands": []}
"playlist_repeat_percent": 50, "stopped": False, "commands": []}
def test_next_wish_leer_und_reihenfolge(client):