feat(random-mix): playlist size selector + filter panel layout cleanup (#445)

* feat(random-mix): playlist size selector + filter panel layout cleanup

Adds a 5-button playlist-size picker (50/75/100/125/150) at the top of
the Random Mix filter panel, persisted via authStore. Clicking a size
immediately reruns the current mix (genre-scoped or All Songs) at the
new size — no second click on Remix needed.

Filter panel layout cleaned up:

- Two sub-sections "MIX SETTINGS" and "EXCLUSIONS" with a divider
  between them so the panel reads cleanly with the new size row.
- Larger panel-level headers (FILTERS / GENRE MIX) so the hierarchy
  panel-title > sub-section is visually unambiguous.
- Italic muted note under MIX SETTINGS calling out that large mix
  sizes may return fewer unique tracks if the server's random pool
  runs short — sets honest expectations instead of users wondering
  why a 150 request returned ~126.

fetchRandomMixSongsUntilFull now scales batch size, max-batch ceiling
and dup-streak budget with target size; when no Settings-level mix
filter is active, the first call asks for the full target so a 150
mix can finish in a single round-trip on most libraries. The loop
falls through to top up with deduped follow-up calls if the server
returns fewer than requested.

* docs(changelog): add #445 Random Mix playlist size selector entry

* chore(credits): add #445 to Psychotoxical contributions
This commit is contained in:
Frank Stellmacher
2026-05-03 19:57:08 +02:00
committed by GitHub
parent 1799e90e04
commit 3b4d54431b
13 changed files with 173 additions and 29 deletions
+4
View File
@@ -323,6 +323,10 @@ export const deTranslation = {
favoriteRemove: 'Aus Favoriten entfernen',
play: 'Abspielen',
trackGenre: 'Genre',
mixSettingsHeader: 'Mix-Einstellungen',
exclusionsHeader: 'Ausschlüsse',
filterPanelInexactSizeNote: 'Die Mix-Größe ist ein Ziel — bei großen Anfragen kann der Server weniger eindeutige Tracks liefern, wenn sein Zufallspool erschöpft ist.',
mixSize: 'Playlistgröße',
excludeAudiobooks: 'Hörbücher & Hörspiele ausschließen',
excludeAudiobooksDesc: 'Prüft Keywords gegen Genre, Titel, Album und Künstler — z. B. Hörbuch, Audiobook, Spoken Word, …',
genreBlocked: 'Keyword gesperrt',
+4
View File
@@ -325,6 +325,10 @@ export const enTranslation = {
favoriteRemove: 'Remove from Favorites',
play: 'Play',
trackGenre: 'Genre',
mixSettingsHeader: 'Mix Settings',
exclusionsHeader: 'Exclusions',
filterPanelInexactSizeNote: 'Mix size is a target — large requests may return fewer unique tracks if the server\'s random pool runs short.',
mixSize: 'Playlist size',
excludeAudiobooks: 'Exclude audiobooks & radio plays',
excludeAudiobooksDesc: 'Matches keywords against genre, title, album, and artist — e.g. Hörbuch, Audiobook, Spoken Word, …',
genreBlocked: 'Keyword blocked',
+4
View File
@@ -324,6 +324,10 @@ export const esTranslation = {
favoriteRemove: 'Quitar de Favoritos',
play: 'Reproducir',
trackGenre: 'Género',
mixSettingsHeader: 'Ajustes del mix',
exclusionsHeader: 'Exclusiones',
filterPanelInexactSizeNote: 'El tamaño del mix es un objetivo — las solicitudes grandes pueden devolver menos pistas únicas si el grupo aleatorio del servidor se agota.',
mixSize: 'Tamaño de lista',
excludeAudiobooks: 'Excluir audiolibros y radioteatros',
excludeAudiobooksDesc: 'Busca palabras clave en género, título, álbum y artista — ej. Hörbuch, Audiobook, Spoken Word, …',
genreBlocked: 'Palabra clave bloqueada',
+4
View File
@@ -323,6 +323,10 @@ export const frTranslation = {
favoriteRemove: 'Retirer des favoris',
play: 'Lire',
trackGenre: 'Genre',
mixSettingsHeader: 'Réglages du mix',
exclusionsHeader: 'Exclusions',
filterPanelInexactSizeNote: 'La taille du mix est une cible — les grandes requêtes peuvent renvoyer moins de morceaux uniques si le pool aléatoire du serveur s\'épuise.',
mixSize: 'Taille de liste',
excludeAudiobooks: 'Exclure les livres audio et pièces radiophoniques',
excludeAudiobooksDesc: 'Correspond aux mots-clés dans le genre, le titre, l\'album et l\'artiste — ex. Hörbuch, Audiobook, Spoken Word, …',
genreBlocked: 'Mot-clé bloqué',
+4
View File
@@ -323,6 +323,10 @@ export const nbTranslation = {
favoriteRemove: 'Fjern fra favoritter',
play: 'Spill',
trackGenre: 'Sjanger',
mixSettingsHeader: 'Miks-innstillinger',
exclusionsHeader: 'Ekskluderinger',
filterPanelInexactSizeNote: 'Miks-størrelsen er et mål — store forespørsler kan returnere færre unike spor hvis serverens tilfeldige pool tar slutt.',
mixSize: 'Listestørrelse',
excludeAudiobooks: 'Ekskluder lydbøker og hørespill',
excludeAudiobooksDesc: 'Samsvarer nøkkelord mot sjanger, tittel, album og artist - f.eks. Hørespill, Lydbok, Spoken Word, …',
genreBlocked: 'Nøkkelord blokkert',
+4
View File
@@ -322,6 +322,10 @@ export const nlTranslation = {
favoriteRemove: 'Uit favorieten verwijderen',
play: 'Afspelen',
trackGenre: 'Genre',
mixSettingsHeader: 'Mixinstellingen',
exclusionsHeader: 'Uitsluitingen',
filterPanelInexactSizeNote: 'De mixgrootte is een doel — grote verzoeken kunnen minder unieke nummers opleveren als de willekeurige pool van de server opraakt.',
mixSize: 'Lijstgrootte',
excludeAudiobooks: 'Luisterboeken en hoorspelen uitsluiten',
excludeAudiobooksDesc: 'Vergelijkt trefwoorden met genre, titel, album en artiest — bijv. Hörbuch, Audiobook, Spoken Word, …',
genreBlocked: 'Trefwoord geblokkeerd',
+4
View File
@@ -340,6 +340,10 @@ export const ruTranslation = {
favoriteRemove: 'Убрать из избранного',
play: 'Играть',
trackGenre: 'Жанр',
mixSettingsHeader: 'Настройки микса',
exclusionsHeader: 'Исключения',
filterPanelInexactSizeNote: 'Размер микса — это цель. При больших запросах сервер может вернуть меньше уникальных треков, если его случайный пул иссякает.',
mixSize: 'Размер списка',
excludeAudiobooks: 'Исключать аудиокниги и радиоспектакли',
excludeAudiobooksDesc:
'По ключевым словам в жанре, названии, альбоме и исполнителе — например Hörbuch, Audiobook, Spoken Word и т. п.',
+4
View File
@@ -321,6 +321,10 @@ export const zhTranslation = {
favoriteRemove: '从收藏中移除',
play: '播放',
trackGenre: '流派',
mixSettingsHeader: '混音设置',
exclusionsHeader: '排除项',
filterPanelInexactSizeNote: '混音大小是一个目标 — 当服务器的随机池耗尽时,较大的请求可能会返回较少的唯一曲目。',
mixSize: '列表大小',
excludeAudiobooks: '排除有声读物和广播剧',
excludeAudiobooksDesc: '根据流派、标题、专辑和艺术家匹配关键词 — 例如 Hörbuch、Audiobook、Spoken Word 等',
genreBlocked: '关键词已屏蔽',