feat(genre): play, shuffle and queue buttons on the genre view (#926)

* feat(genre): add paginated songs-by-genre API

Wraps the Subsonic getSongsByGenre endpoint plus a fetchAllSongsByGenre
helper that paginates until exhausted, capped to keep the queue and the
burst of requests bounded for very large genres.

* refactor(playback): extract shared bulk play/shuffle/enqueue helper

A single fetchTracks-driven core (loading flag, empty guard, canonical
shuffleArray) so async detail-page play buttons stop growing divergent
copies. Artist detail now reuses it, dropping its weaker sort-random
shuffle.

* feat(genre): play, shuffle and queue buttons on the genre view

Header buttons load the genre's songs and start ordered or shuffled
playback, or append them to the queue. The slice is bounded to stay
within the queue resolver's cache budget so every row resolves instead
of rendering as a placeholder. Strings added across all nine locales.

* docs(changelog): genre play/shuffle buttons (#926)
This commit is contained in:
Frank Stellmacher
2026-05-30 12:59:44 +02:00
committed by GitHub
parent 6c74cae0b7
commit e734a8fc43
16 changed files with 276 additions and 20 deletions
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'Keine Alben in diesem Genre gefunden.',
loadMore: 'Mehr laden',
back: 'Zurück',
shuffle: 'Zufallswiedergabe',
addToQueue: 'Zur Warteschlange',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'No albums found for this genre.',
loadMore: 'Load more',
back: 'Back',
shuffle: 'Shuffle',
addToQueue: 'Add to queue',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'No se encontraron álbumes para este género.',
loadMore: 'Cargar más',
back: 'Volver',
shuffle: 'Aleatorio',
addToQueue: 'Agregar a la cola',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'Aucun album trouvé pour ce genre.',
loadMore: 'Charger plus',
back: 'Retour',
shuffle: 'Aléatoire',
addToQueue: 'Ajouter à la file',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'Ingen album funnet for denne sjangeren.',
loadMore: 'Last mer',
back: 'Tilbake',
shuffle: 'Bland',
addToQueue: 'Legg til i kø',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'Geen albums gevonden voor dit genre.',
loadMore: 'Meer laden',
back: 'Terug',
shuffle: 'Willekeurig',
addToQueue: 'Aan wachtrij toevoegen',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: 'Niciun album găsit pentru acest gen.',
loadMore: 'Încarcă mai mult',
back: 'Înapoi',
shuffle: 'Amestecă',
addToQueue: 'Adaugă la coadă',
};
+2
View File
@@ -11,4 +11,6 @@ export const genres = {
albumsEmpty: 'В этом жанре альбомов нет.',
loadMore: 'Ещё',
back: 'Назад',
shuffle: 'Перемешать',
addToQueue: 'В очередь',
};
+2
View File
@@ -9,4 +9,6 @@ export const genres = {
albumsEmpty: '未找到该流派的专辑。',
loadMore: '加载更多',
back: '返回',
shuffle: '随机播放',
addToQueue: '添加到队列',
};