feat: v1.26.0 — Bulk Select, Song Info, Favorite Button, Recently Played

### Added
- Favorite/Star button in player bar (requested by @halfkey)
- Bulk multi-select for album tracklist and playlist detail (add to playlist, remove from playlist)
- Song Info modal via right-click context menu (metadata: format, bitrate, sample rate, bit depth, channels, file size, path, replay gain)
- Recently Played section on Home page
- "Show activity in Now Playing" opt-in toggle in Settings → Behavior

### Fixed
- Queue cover art not updating on track change (useCachedUrl/CachedImage reset on cacheKey change)
- FullscreenPlayer background flickering (FsBg preloads image before layer transition)
- Playlist card delete confirmation visual (size expansion + pulse animation)
- Gruvbox Light Soft: back button and badge invisible against light background

### Changed
- buildStreamUrl: removed unused suffix parameter

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-01 20:57:28 +02:00
parent 7d1c66071e
commit 434ee0ecf1
26 changed files with 1074 additions and 221 deletions
+160
View File
@@ -31,6 +31,7 @@ const enTranslation = {
starred: 'Personal Favorites',
recent: 'Recently Added',
mostPlayed: 'Most Played',
recentlyPlayed: 'Recently Played',
discover: 'Discover',
loadMore: 'Load More',
discoverMore: 'Discover More',
@@ -102,6 +103,7 @@ const enTranslation = {
goToArtist: 'Go to Artist',
download: 'Download (ZIP)',
addToPlaylist: 'Add to Playlist',
songInfo: 'Song Info',
},
albumDetail: {
back: 'Back',
@@ -300,6 +302,10 @@ const enTranslation = {
filterSearchGenres: 'Search genres…',
filterNoGenres: 'No genres match',
filterClear: 'Clear',
bulkSelected: '{{count}} selected',
bulkAddToPlaylist: 'Add to Playlist',
bulkRemoveFromPlaylist: 'Remove from Playlist',
bulkClear: 'Clear selection',
},
settings: {
title: 'Settings',
@@ -363,6 +369,8 @@ const enTranslation = {
cacheClearCancel: 'Cancel',
minimizeToTray: 'Minimize to Tray',
minimizeToTrayDesc: 'When closing the window, keep Psysonic running in the system tray instead of quitting.',
nowPlayingEnabled: 'Show in Now Playing',
nowPlayingEnabledDesc: 'Broadcast your currently playing track to the server\'s live listener view. Disable to stop sending playback data.',
downloadsTitle: 'Download Folder',
downloadsDefault: 'Default Downloads Folder',
pickFolder: 'Select',
@@ -600,6 +608,29 @@ const enTranslation = {
lyricsLoading: 'Loading lyrics…',
lyricsNotFound: 'No lyrics found for this track',
},
songInfo: {
title: 'Song Info',
songTitle: 'Title',
artist: 'Artist',
album: 'Album',
albumArtist: 'Album Artist',
year: 'Year',
genre: 'Genre',
duration: 'Duration',
track: 'Track',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Sample Rate',
bitDepth: 'Bit Depth',
channels: 'Channels',
fileSize: 'File Size',
path: 'Path',
replayGainTrack: 'RG Track Gain',
replayGainAlbum: 'RG Album Gain',
replayGainPeak: 'RG Track Peak',
mono: 'Mono',
stereo: 'Stereo',
},
playlists: {
title: 'Playlists',
newPlaylist: 'New Playlist',
@@ -612,6 +643,7 @@ const enTranslation = {
notFound: 'Playlist not found.',
songs: '{{n}} songs',
playAll: 'Play All',
shuffle: 'Shuffle',
addToQueue: 'Add to Queue',
back: 'Back to Playlists',
deletePlaylist: 'Delete',
@@ -658,6 +690,7 @@ const deTranslation = {
starred: 'Persönliche Favoriten',
recent: 'Zuletzt hinzugefügt',
mostPlayed: 'Meistgehört',
recentlyPlayed: 'Kürzlich gespielt',
discover: 'Entdecken',
loadMore: 'Mehr laden',
discoverMore: 'Mehr entdecken',
@@ -729,6 +762,7 @@ const deTranslation = {
goToArtist: 'Zum Künstler',
download: 'Herunterladen (ZIP)',
addToPlaylist: 'Zur Playlist hinzufügen',
songInfo: 'Song-Infos',
},
albumDetail: {
back: 'Zurück',
@@ -927,6 +961,10 @@ const deTranslation = {
filterSearchGenres: 'Genres suchen…',
filterNoGenres: 'Keine Genres gefunden',
filterClear: 'Zurücksetzen',
bulkSelected: '{{count}} ausgewählt',
bulkAddToPlaylist: 'Zur Playlist hinzufügen',
bulkRemoveFromPlaylist: 'Aus Playlist entfernen',
bulkClear: 'Auswahl aufheben',
},
settings: {
title: 'Einstellungen',
@@ -990,6 +1028,8 @@ const deTranslation = {
cacheClearCancel: 'Abbrechen',
minimizeToTray: 'Im Tray minimieren',
minimizeToTrayDesc: 'Beim Schließen des Fensters läuft Psysonic weiter im System-Tray statt zu beenden.',
nowPlayingEnabled: 'Im Livefenster anzeigen',
nowPlayingEnabledDesc: 'Überträgt den aktuell gespielten Titel an die Livehörer-Ansicht des Servers. Deaktivieren, um keine Wiedergabedaten zu senden.',
downloadsTitle: 'Download-Ordner',
downloadsDefault: 'Standard-Downloads-Ordner',
pickFolder: 'Auswählen',
@@ -1227,6 +1267,29 @@ const deTranslation = {
lyricsLoading: 'Lyrics werden geladen…',
lyricsNotFound: 'Keine Lyrics für diesen Titel gefunden',
},
songInfo: {
title: 'Song-Infos',
songTitle: 'Titel',
artist: 'Künstler',
album: 'Album',
albumArtist: 'Album-Künstler',
year: 'Jahr',
genre: 'Genre',
duration: 'Länge',
track: 'Track',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Abtastrate',
bitDepth: 'Bittiefe',
channels: 'Kanäle',
fileSize: 'Dateigröße',
path: 'Speicherort',
replayGainTrack: 'RG Track Gain',
replayGainAlbum: 'RG Album Gain',
replayGainPeak: 'RG Track Peak',
mono: 'Mono',
stereo: 'Stereo',
},
playlists: {
title: 'Playlists',
newPlaylist: 'Neue Playlist',
@@ -1239,6 +1302,7 @@ const deTranslation = {
notFound: 'Playlist nicht gefunden.',
songs: '{{n}} Songs',
playAll: 'Alle abspielen',
shuffle: 'Zufallswiedergabe',
addToQueue: 'Zur Warteschlange',
back: 'Zurück zu Playlists',
deletePlaylist: 'Löschen',
@@ -1285,6 +1349,7 @@ const frTranslation = {
starred: 'Favoris personnels',
recent: 'Ajoutés récemment',
mostPlayed: 'Les plus écoutés',
recentlyPlayed: 'Récemment écoutés',
discover: 'Découvrir',
loadMore: 'Charger plus',
discoverMore: 'Découvrir plus',
@@ -1356,6 +1421,7 @@ const frTranslation = {
goToArtist: 'Aller à l\'artiste',
download: 'Télécharger (ZIP)',
addToPlaylist: 'Ajouter à la playlist',
songInfo: 'Infos du morceau',
},
albumDetail: {
back: 'Retour',
@@ -1554,6 +1620,10 @@ const frTranslation = {
filterSearchGenres: 'Rechercher des genres…',
filterNoGenres: 'Aucun genre trouvé',
filterClear: 'Effacer',
bulkSelected: '{{count}} sélectionné(s)',
bulkAddToPlaylist: 'Ajouter à la playlist',
bulkRemoveFromPlaylist: 'Retirer de la playlist',
bulkClear: 'Désélectionner',
},
settings: {
title: 'Paramètres',
@@ -1617,6 +1687,8 @@ const frTranslation = {
cacheClearCancel: 'Annuler',
minimizeToTray: 'Réduire dans la barre système',
minimizeToTrayDesc: 'Lors de la fermeture, Psysonic continue de fonctionner dans la barre système au lieu de se fermer.',
nowPlayingEnabled: 'Afficher dans la fenêtre live',
nowPlayingEnabledDesc: 'Diffuse le titre en cours de lecture vers la vue des auditeurs en direct du serveur. Désactiver pour ne pas envoyer de données de lecture.',
downloadsTitle: 'Dossier de téléchargement',
downloadsDefault: 'Dossier de téléchargement par défaut',
pickFolder: 'Sélectionner',
@@ -1854,6 +1926,29 @@ const frTranslation = {
lyricsLoading: 'Chargement des paroles…',
lyricsNotFound: 'Aucune parole trouvée pour ce titre',
},
songInfo: {
title: 'Infos du morceau',
songTitle: 'Titre',
artist: 'Artiste',
album: 'Album',
albumArtist: 'Artiste de l\'album',
year: 'Année',
genre: 'Genre',
duration: 'Durée',
track: 'Piste',
format: 'Format',
bitrate: 'Débit',
sampleRate: 'Fréquence d\'échantillonnage',
bitDepth: 'Profondeur de bits',
channels: 'Canaux',
fileSize: 'Taille du fichier',
path: 'Emplacement',
replayGainTrack: 'RG Gain de piste',
replayGainAlbum: 'RG Gain d\'album',
replayGainPeak: 'RG Crête de piste',
mono: 'Mono',
stereo: 'Stéréo',
},
playlists: {
title: 'Playlists',
newPlaylist: 'Nouvelle playlist',
@@ -1866,6 +1961,7 @@ const frTranslation = {
notFound: 'Playlist introuvable.',
songs: '{{n}} titres',
playAll: 'Tout lire',
shuffle: 'Aléatoire',
addToQueue: 'Ajouter à la file',
back: 'Retour aux playlists',
deletePlaylist: 'Supprimer',
@@ -1912,6 +2008,7 @@ const nlTranslation = {
starred: 'Persoonlijke favorieten',
recent: 'Recent toegevoegd',
mostPlayed: 'Meest gespeeld',
recentlyPlayed: 'Recent afgespeeld',
discover: 'Ontdekken',
loadMore: 'Meer laden',
discoverMore: 'Meer ontdekken',
@@ -1983,6 +2080,7 @@ const nlTranslation = {
goToArtist: 'Naar artiest',
download: 'Downloaden (ZIP)',
addToPlaylist: 'Toevoegen aan playlist',
songInfo: 'Nummerinfo',
},
albumDetail: {
back: 'Terug',
@@ -2181,6 +2279,10 @@ const nlTranslation = {
filterSearchGenres: 'Genres zoeken…',
filterNoGenres: 'Geen genres gevonden',
filterClear: 'Wissen',
bulkSelected: '{{count}} geselecteerd',
bulkAddToPlaylist: 'Toevoegen aan afspeellijst',
bulkRemoveFromPlaylist: 'Verwijderen uit afspeellijst',
bulkClear: 'Selectie wissen',
},
settings: {
title: 'Instellingen',
@@ -2244,6 +2346,8 @@ const nlTranslation = {
cacheClearCancel: 'Annuleren',
minimizeToTray: 'Minimaliseren naar systeemvak',
minimizeToTrayDesc: 'Bij het sluiten van het venster blijft Psysonic actief in het systeemvak in plaats van af te sluiten.',
nowPlayingEnabled: 'Weergeven in live-venster',
nowPlayingEnabledDesc: 'Stuurt het huidige nummer naar de live-luisteraarsweergave van de server. Uitschakelen om geen afspeelgegevens te verzenden.',
downloadsTitle: 'Downloadmap',
downloadsDefault: 'Standaard downloadmap',
pickFolder: 'Selecteren',
@@ -2481,6 +2585,29 @@ const nlTranslation = {
lyricsLoading: 'Songtekst laden…',
lyricsNotFound: 'Geen songtekst gevonden voor dit nummer',
},
songInfo: {
title: 'Nummerinfo',
songTitle: 'Titel',
artist: 'Artiest',
album: 'Album',
albumArtist: 'Albumartiest',
year: 'Jaar',
genre: 'Genre',
duration: 'Duur',
track: 'Track',
format: 'Formaat',
bitrate: 'Bitrate',
sampleRate: 'Samplefrequentie',
bitDepth: 'Bitdiepte',
channels: 'Kanalen',
fileSize: 'Bestandsgrootte',
path: 'Locatie',
replayGainTrack: 'RG Track Gain',
replayGainAlbum: 'RG Album Gain',
replayGainPeak: 'RG Track Peak',
mono: 'Mono',
stereo: 'Stereo',
},
playlists: {
title: 'Playlists',
newPlaylist: 'Nieuwe playlist',
@@ -2493,6 +2620,7 @@ const nlTranslation = {
notFound: 'Playlist niet gevonden.',
songs: '{{n}} nummers',
playAll: 'Alles afspelen',
shuffle: 'Willekeurig',
addToQueue: 'Aan wachtrij toevoegen',
back: 'Terug naar playlists',
deletePlaylist: 'Verwijderen',
@@ -2539,6 +2667,7 @@ const zhTranslation = {
starred: '个人收藏',
recent: '最近添加',
mostPlayed: '最常播放',
recentlyPlayed: '最近播放',
discover: '发现',
loadMore: '加载更多',
discoverMore: '发现更多',
@@ -2610,6 +2739,7 @@ const zhTranslation = {
goToArtist: '前往艺术家',
download: '下载 (ZIP)',
addToPlaylist: '添加到播放列表',
songInfo: '歌曲信息',
},
albumDetail: {
back: '返回',
@@ -2808,6 +2938,10 @@ const zhTranslation = {
filterSearchGenres: '搜索流派…',
filterNoGenres: '未找到匹配流派',
filterClear: '清除',
bulkSelected: '已选 {{count}} 首',
bulkAddToPlaylist: '添加到播放列表',
bulkRemoveFromPlaylist: '从播放列表移除',
bulkClear: '取消选择',
},
settings: {
title: '设置',
@@ -2871,6 +3005,8 @@ const zhTranslation = {
cacheClearCancel: '取消',
minimizeToTray: '最小化到托盘',
minimizeToTrayDesc: '关闭窗口时,Psysonic 将继续在系统托盘中运行,而不是退出。',
nowPlayingEnabled: '在实时窗口中显示',
nowPlayingEnabledDesc: '将当前播放的曲目广播到服务器的实时听众视图。禁用以停止发送播放数据。',
downloadsTitle: '下载文件夹',
downloadsDefault: '默认下载文件夹',
pickFolder: '选择',
@@ -3108,6 +3244,29 @@ const zhTranslation = {
lyricsLoading: '正在加载歌词…',
lyricsNotFound: '未找到此曲目的歌词',
},
songInfo: {
title: '歌曲信息',
songTitle: '标题',
artist: '艺术家',
album: '专辑',
albumArtist: '专辑艺术家',
year: '年份',
genre: '流派',
duration: '时长',
track: '曲目',
format: '格式',
bitrate: '比特率',
sampleRate: '采样率',
bitDepth: '位深度',
channels: '声道',
fileSize: '文件大小',
path: '文件路径',
replayGainTrack: 'RG 曲目增益',
replayGainAlbum: 'RG 专辑增益',
replayGainPeak: 'RG 曲目峰值',
mono: '单声道',
stereo: '立体声',
},
playlists: {
title: '播放列表',
newPlaylist: '新建播放列表',
@@ -3120,6 +3279,7 @@ const zhTranslation = {
notFound: '未找到播放列表。',
songs: '{{n}} 首歌曲',
playAll: '全部播放',
shuffle: '随机播放',
addToQueue: '添加到队列',
back: '返回播放列表',
deletePlaylist: '删除',