feat: v1.30.0 — Discord RPC, offline bulk download, artist images, lazy loading, crossfade fix

- Discord Rich Presence (opt-in) — requested by @Bewenben (#49)
- Bulk offline download for playlists and artist discographies — requested by @Apollosport (#54)
- Offline Library filter tabs: All / Albums / Playlists / Discographies with artist grouping
- Artist images on Artists overview (opt-in, off by default) — reported by @Apollosport (#53)
- Image lazy loading via IntersectionObserver (300px margin) across all pages
- Fix: crossfade no longer triggers on manual track skip — reported by @netherguy4 (#35)
- Fix: playlist offline cache now stored as single entry (not per-album)
- Fix: image cache AbortController no longer blocks IDB writes
- Update toast: experimental auto-update hint + GH download link always visible (Win/Mac)
- Queue tech strip: genre removed
- Facebook theme: contrast, opaque badge/back button, queue tab labels
- "Save discography offline" label (was "Download discography")
- Fix: clearing empty playlists via updatePlaylist.view (Axios empty array workaround)
- starredOverrides propagated to AlbumDetail, Favorites, RandomMix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-03 14:53:19 +02:00
parent 651b3cb050
commit c365140870
29 changed files with 1099 additions and 338 deletions
+86
View File
@@ -140,6 +140,7 @@ const enTranslation = {
bioModal: 'Artist Biography',
bioClose: 'Close',
ratingLabel: 'Rating',
enlargeCover: 'Enlarge',
},
artistDetail: {
back: 'Back',
@@ -165,6 +166,9 @@ const enTranslation = {
openedInBrowser: 'Opened in browser',
featuredOn: 'Also Featured On',
similarArtists: 'Similar Artists',
cacheOffline: 'Save discography offline',
offlineCached: 'Discography cached',
offlineDownloading: 'Caching… ({{done}}/{{total}} albums)',
},
favorites: {
title: 'Favorites',
@@ -237,6 +241,8 @@ const enTranslation = {
all: 'All',
gridView: 'Grid view',
listView: 'List view',
imagesOn: 'Artist images on — may increase network and system load',
imagesOff: 'Artist images off — showing initials only',
loadMore: 'Load more',
notFound: 'No artists found.',
albumCount_one: '{{count}} Album',
@@ -275,6 +281,10 @@ const enTranslation = {
offlineLibraryEmpty: 'No albums cached yet. Go online, open an album and click "Make available offline".',
offlineAlbumCount: '{{n}} album',
offlineAlbumCount_plural: '{{n}} albums',
offlineFilterAll: 'All',
offlineFilterAlbums: 'Albums',
offlineFilterPlaylists: 'Playlists',
offlineFilterArtists: 'Discographies',
retry: 'Retry',
lastfmConnected: 'Last.fm connected as @{{user}}',
lastfmSessionInvalid: 'Session invalid — click to re-connect',
@@ -313,6 +323,7 @@ const enTranslation = {
updaterDownloading: 'Downloading…',
updaterInstalling: 'Installing…',
updaterDownload: 'Download from GitHub',
updaterExperimentalHint: 'Auto-update is still in development',
},
settings: {
title: 'Settings',
@@ -374,8 +385,12 @@ const enTranslation = {
cacheClearWarning: 'This will also remove all offline albums from the library.',
cacheClearConfirm: 'Clear Everything',
cacheClearCancel: 'Cancel',
showArtistImages: 'Show Artist Images',
showArtistImagesDesc: 'Load and display artist images in the Artists overview. Disabled by default to reduce server disk I/O and network load on large libraries.',
minimizeToTray: 'Minimize to Tray',
minimizeToTrayDesc: 'When closing the window, keep Psysonic running in the system tray instead of quitting.',
discordRichPresence: 'Discord Rich Presence',
discordRichPresenceDesc: 'Show the currently playing track on your Discord profile. Requires Discord to be running.',
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',
@@ -672,6 +687,9 @@ const enTranslation = {
titleBadge: 'Playlist',
refreshSuggestions: 'New suggestions',
addSong: 'Add to playlist',
cacheOffline: 'Cache playlist offline',
offlineCached: 'Playlist cached',
offlineDownloading: 'Caching… ({{done}}/{{total}} albums)',
}
};
@@ -814,6 +832,7 @@ const deTranslation = {
bioModal: 'Künstler-Biografie',
bioClose: 'Schließen',
ratingLabel: 'Bewertung',
enlargeCover: 'Vergrößern',
},
artistDetail: {
back: 'Zurück',
@@ -839,6 +858,9 @@ const deTranslation = {
openedInBrowser: 'Im Browser geöffnet',
featuredOn: 'Auch enthalten auf',
similarArtists: 'Ähnliche Künstler',
cacheOffline: 'Diskografie offline speichern',
offlineCached: 'Diskografie gecacht',
offlineDownloading: 'Wird gecacht… ({{done}}/{{total}} Alben)',
},
favorites: {
title: 'Favoriten',
@@ -911,6 +933,8 @@ const deTranslation = {
all: 'Alle',
gridView: 'Gitteransicht',
listView: 'Listenansicht',
imagesOn: 'Künstlerbilder aktiv — kann Netzwerk- und Systemlast erhöhen',
imagesOff: 'Künstlerbilder deaktiviert — zeigt nur Initialen',
loadMore: 'Mehr laden',
notFound: 'Keine Künstler gefunden.',
albumCount_one: '{{count}} Album',
@@ -949,6 +973,10 @@ const deTranslation = {
offlineLibraryEmpty: 'Noch keine Alben gecacht. Online gehen, Album öffnen und "Offline verfügbar machen" klicken.',
offlineAlbumCount: '{{n}} Album',
offlineAlbumCount_plural: '{{n}} Alben',
offlineFilterAll: 'Alle',
offlineFilterAlbums: 'Alben',
offlineFilterPlaylists: 'Playlists',
offlineFilterArtists: 'Diskografien',
retry: 'Erneut versuchen',
lastfmConnected: 'Last.fm verbunden als @{{user}}',
lastfmSessionInvalid: 'Session ungültig — klicken zum Neu-Verbinden',
@@ -987,6 +1015,7 @@ const deTranslation = {
updaterDownloading: 'Wird geladen…',
updaterInstalling: 'Wird installiert…',
updaterDownload: 'Von GitHub herunterladen',
updaterExperimentalHint: 'Automatische Updates sind noch in Entwicklung',
},
settings: {
title: 'Einstellungen',
@@ -1048,8 +1077,12 @@ const deTranslation = {
cacheClearWarning: 'Alle Offline-Alben werden damit aus der Bibliothek entfernt.',
cacheClearConfirm: 'Alles löschen',
cacheClearCancel: 'Abbrechen',
showArtistImages: 'Künstlerbilder anzeigen',
showArtistImagesDesc: 'Lädt und zeigt Künstlerbilder in der Künstlerübersicht. Standardmäßig deaktiviert, um Server-I/O und Netzwerklast bei großen Bibliotheken zu reduzieren.',
minimizeToTray: 'Im Tray minimieren',
minimizeToTrayDesc: 'Beim Schließen des Fensters läuft Psysonic weiter im System-Tray statt zu beenden.',
discordRichPresence: 'Discord Rich Presence',
discordRichPresenceDesc: 'Zeigt den aktuell gespielten Titel im Discord-Profil an. Discord muss dafür geöffnet sein.',
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',
@@ -1346,6 +1379,9 @@ const deTranslation = {
titleBadge: 'Playlist',
refreshSuggestions: 'Neue Vorschläge',
addSong: 'Zur Playlist hinzufügen',
cacheOffline: 'Playlist offline speichern',
offlineCached: 'Playlist gecacht',
offlineDownloading: 'Wird gecacht… ({{done}}/{{total}} Alben)',
}
};
@@ -1488,6 +1524,7 @@ const frTranslation = {
bioModal: 'Biographie de l\'artiste',
bioClose: 'Fermer',
ratingLabel: 'Note',
enlargeCover: 'Agrandir',
},
artistDetail: {
back: 'Retour',
@@ -1513,6 +1550,9 @@ const frTranslation = {
openedInBrowser: 'Ouvert dans le navigateur',
featuredOn: 'Également sur',
similarArtists: 'Artistes similaires',
cacheOffline: 'Enregistrer la discographie hors ligne',
offlineCached: 'Discographie en cache',
offlineDownloading: 'En cache… ({{done}}/{{total}} albums)',
},
favorites: {
title: 'Favoris',
@@ -1585,6 +1625,8 @@ const frTranslation = {
all: 'Tous',
gridView: 'Vue en grille',
listView: 'Vue en liste',
imagesOn: 'Images d\'artistes activées — peut augmenter la charge réseau et système',
imagesOff: 'Images d\'artistes désactivées — affichage des initiales uniquement',
loadMore: 'Charger plus',
notFound: 'Aucun artiste trouvé.',
albumCount_one: '{{count}} album',
@@ -1623,6 +1665,10 @@ const frTranslation = {
offlineLibraryEmpty: 'Aucun album en cache. Connectez-vous, ouvrez un album et cliquez sur "Rendre disponible hors ligne".',
offlineAlbumCount: '{{n}} album',
offlineAlbumCount_plural: '{{n}} albums',
offlineFilterAll: 'Tout',
offlineFilterAlbums: 'Albums',
offlineFilterPlaylists: 'Playlists',
offlineFilterArtists: 'Discographies',
retry: 'Réessayer',
lastfmConnected: 'Last.fm connecté en tant que @{{user}}',
lastfmSessionInvalid: 'Session invalide — cliquez pour vous reconnecter',
@@ -1661,6 +1707,7 @@ const frTranslation = {
updaterDownloading: 'Téléchargement…',
updaterInstalling: 'Installation…',
updaterDownload: 'Télécharger depuis GitHub',
updaterExperimentalHint: 'La mise à jour automatique est encore en développement',
},
settings: {
title: 'Paramètres',
@@ -1722,8 +1769,12 @@ const frTranslation = {
cacheClearWarning: 'Cela supprimera aussi tous les albums hors ligne de la bibliothèque.',
cacheClearConfirm: 'Tout supprimer',
cacheClearCancel: 'Annuler',
showArtistImages: 'Afficher les images d\'artistes',
showArtistImagesDesc: 'Charge et affiche les images d\'artistes dans la vue d\'ensemble. Désactivé par défaut pour réduire les E/S disque serveur et la charge réseau sur les grandes bibliothèques.',
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.',
discordRichPresence: 'Discord Rich Presence',
discordRichPresenceDesc: 'Affiche le titre en cours de lecture sur votre profil Discord. Discord doit être ouvert.',
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',
@@ -2020,6 +2071,9 @@ const frTranslation = {
titleBadge: 'Playlist',
refreshSuggestions: 'Nouvelles suggestions',
addSong: 'Ajouter à la playlist',
cacheOffline: 'Mettre la playlist hors ligne',
offlineCached: 'Playlist en cache',
offlineDownloading: 'En cache… ({{done}}/{{total}} albums)',
}
};
@@ -2162,6 +2216,7 @@ const nlTranslation = {
bioModal: 'Artiest biografie',
bioClose: 'Sluiten',
ratingLabel: 'Beoordeling',
enlargeCover: 'Vergroten',
},
artistDetail: {
back: 'Terug',
@@ -2187,6 +2242,9 @@ const nlTranslation = {
openedInBrowser: 'Geopend in browser',
featuredOn: 'Ook te vinden op',
similarArtists: 'Vergelijkbare artiesten',
cacheOffline: 'Discografie offline opslaan',
offlineCached: 'Discografie gecached',
offlineDownloading: 'Cachen… ({{done}}/{{total}} albums)',
},
favorites: {
title: 'Favorieten',
@@ -2259,6 +2317,8 @@ const nlTranslation = {
all: 'Alle',
gridView: 'Rasterweergave',
listView: 'Lijstweergave',
imagesOn: 'Artiestafbeeldingen aan — kan netwerk- en systeembelasting verhogen',
imagesOff: 'Artiestafbeeldingen uit — toont alleen initialen',
loadMore: 'Meer laden',
notFound: 'Geen artiesten gevonden.',
albumCount_one: '{{count}} album',
@@ -2297,6 +2357,10 @@ const nlTranslation = {
offlineLibraryEmpty: 'Nog geen albums gecached. Ga online, open een album en klik op "Offline beschikbaar maken".',
offlineAlbumCount: '{{n}} album',
offlineAlbumCount_plural: '{{n}} albums',
offlineFilterAll: 'Alles',
offlineFilterAlbums: 'Albums',
offlineFilterPlaylists: 'Afspeellijsten',
offlineFilterArtists: 'Discografieën',
retry: 'Opnieuw proberen',
lastfmConnected: 'Last.fm verbonden als @{{user}}',
lastfmSessionInvalid: 'Sessie ongeldig — klik om opnieuw te verbinden',
@@ -2335,6 +2399,7 @@ const nlTranslation = {
updaterDownloading: 'Downloaden…',
updaterInstalling: 'Installeren…',
updaterDownload: 'Downloaden van GitHub',
updaterExperimentalHint: 'Automatisch bijwerken is nog in ontwikkeling',
},
settings: {
title: 'Instellingen',
@@ -2396,8 +2461,12 @@ const nlTranslation = {
cacheClearWarning: 'Dit verwijdert ook alle offline albums uit de bibliotheek.',
cacheClearConfirm: 'Alles wissen',
cacheClearCancel: 'Annuleren',
showArtistImages: 'Artiestafbeeldingen weergeven',
showArtistImagesDesc: 'Laadt en toont artiestafbeeldingen in het artiestenoverzicht. Standaard uitgeschakeld om server-I/O en netwerkbelasting bij grote bibliotheken te beperken.',
minimizeToTray: 'Minimaliseren naar systeemvak',
minimizeToTrayDesc: 'Bij het sluiten van het venster blijft Psysonic actief in het systeemvak in plaats van af te sluiten.',
discordRichPresence: 'Discord Rich Presence',
discordRichPresenceDesc: 'Toont het huidige nummer op je Discord-profiel. Discord moet daarvoor geopend zijn.',
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',
@@ -2694,6 +2763,9 @@ const nlTranslation = {
titleBadge: 'Playlist',
refreshSuggestions: 'Nieuwe suggesties',
addSong: 'Toevoegen aan playlist',
cacheOffline: 'Playlist offline opslaan',
offlineCached: 'Playlist gecached',
offlineDownloading: 'Cachen… ({{done}}/{{total}} albums)',
}
};
@@ -2836,6 +2908,7 @@ const zhTranslation = {
bioModal: '艺术家简介',
bioClose: '关闭',
ratingLabel: '评分',
enlargeCover: '放大',
},
artistDetail: {
back: '返回',
@@ -2861,6 +2934,9 @@ const zhTranslation = {
openedInBrowser: '已在浏览器中打开',
featuredOn: '还出现在',
similarArtists: '相似艺术家',
cacheOffline: '离线保存全部专辑',
offlineCached: '全部专辑已缓存',
offlineDownloading: '正在缓存… ({{done}}/{{total}} 张专辑)',
},
favorites: {
title: '收藏夹',
@@ -2933,6 +3009,8 @@ const zhTranslation = {
all: '全部',
gridView: '网格视图',
listView: '列表视图',
imagesOn: '艺术家图片已开启 — 可能增加网络和系统负载',
imagesOff: '艺术家图片已关闭 — 仅显示首字母',
loadMore: '加载更多',
notFound: '未找到艺术家。',
albumCount_one: '{{count}} 张专辑',
@@ -3009,6 +3087,7 @@ const zhTranslation = {
updaterDownloading: '下载中…',
updaterInstalling: '安装中…',
updaterDownload: '从 GitHub 下载',
updaterExperimentalHint: '自动更新功能仍在开发中',
},
settings: {
title: '设置',
@@ -3070,8 +3149,12 @@ const zhTranslation = {
cacheClearWarning: '这将同时移除音乐库中的所有离线专辑。',
cacheClearConfirm: '全部清除',
cacheClearCancel: '取消',
showArtistImages: '显示艺术家图片',
showArtistImagesDesc: '在艺术家概览中加载并显示艺术家图片。默认关闭以减少大型音乐库的服务器磁盘I/O和网络负载。',
minimizeToTray: '最小化到托盘',
minimizeToTrayDesc: '关闭窗口时,Psysonic 将继续在系统托盘中运行,而不是退出。',
discordRichPresence: 'Discord Rich Presence',
discordRichPresenceDesc: '在 Discord 个人资料上显示当前播放的曲目。需要 Discord 处于运行状态。',
nowPlayingEnabled: '在实时窗口中显示',
nowPlayingEnabledDesc: '将当前播放的曲目广播到服务器的实时听众视图。禁用以停止发送播放数据。',
downloadsTitle: '下载文件夹',
@@ -3368,6 +3451,9 @@ const zhTranslation = {
titleBadge: '播放列表',
refreshSuggestions: '新建议',
addSong: '添加到播放列表',
cacheOffline: '离线缓存播放列表',
offlineCached: '播放列表已缓存',
offlineDownloading: '正在缓存… ({{done}}/{{total}} 张专辑)',
}
};