feat(tracklist): play count, last played, BPM columns + Song Info rows (#730)

* feat(tracklist): play count, last played, and BPM columns (#516)

Adds three opt-in columns to Album / Playlist / Favorites tracklists,
plus the same fields in the Song Info modal. Picks up Navidrome's
existing `playCount` / `played` / `bpm` from the Subsonic response — no
new API calls.

- `SubsonicSong` gains `playCount`, `played`, `bpm` (already populated
  by Navidrome's Subsonic API, just unmapped in the TS model).
- `albumTrackListHelpers.COLUMNS`, `PL_COLUMNS` (PlaylistDetail), and
  `FAV_COLUMNS` (Favorites) get the three new entries. Genre also added
  to the playlist column set for parity with the other two lists.
- Render uses `.track-duration` (12px tabular, centered, muted) for the
  numeric stats and `.track-genre` (11px text, muted) for the relative
  last-played timestamp via the existing `formatLastSeen` helper.
- Sort logic extended in `playlistDisplayedSongs`,
  `useAlbumDetailSort`, and `useFavoritesSongFiltering` for the three
  new keys.
- `SongInfoModal` gets matching rows (BPM / Play count / Last played).
- `useTracklistColumns.gridTemplate` / `gridMinWidth` fall back to the
  ColDef's `defaultWidth` when a visible column has no saved width
  (newly added column on an old prefs blob would otherwise emit
  `undefinedpx` and collapse the row layout until reset-to-defaults).
- BPM cells skip the rendering when Navidrome returns 0 (default for
  untagged files) — show `—` instead of `0`.
- i18n: `albumDetail.trackPlayCount / trackLastPlayed / trackBpm` and
  `songInfo.playCount / lastPlayed / bpm` in all 9 locales.

Suggested by jbigginswyl (#516).

* docs(changelog): tracklist Plays / Last played / BPM columns (#730)
This commit is contained in:
Frank Stellmacher
2026-05-15 22:01:05 +02:00
committed by GitHub
parent 603660d407
commit efd85ffde3
31 changed files with 189 additions and 36 deletions
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Interpret',
trackGenre: 'Genre',
trackPlayCount: 'Wiedergaben',
trackLastPlayed: 'Zuletzt gespielt',
trackBpm: 'BPM',
trackFormat: 'Format',
trackFavorite: 'Favorit',
trackRating: 'Bewertung',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Genre',
duration: 'Länge',
track: 'Track',
bpm: 'BPM',
playCount: 'Wiedergaben',
lastPlayed: 'Zuletzt gespielt',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Abtastrate',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Artist',
trackGenre: 'Genre',
trackPlayCount: 'Plays',
trackLastPlayed: 'Last played',
trackBpm: 'BPM',
trackFormat: 'Format',
trackFavorite: 'Favorite',
trackRating: 'Rating',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Genre',
duration: 'Duration',
track: 'Track',
bpm: 'BPM',
playCount: 'Play count',
lastPlayed: 'Last played',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Sample Rate',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Álbum',
trackArtist: 'Artista',
trackGenre: 'Género',
trackPlayCount: 'Reproducciones',
trackLastPlayed: 'Última reproducción',
trackBpm: 'BPM',
trackFormat: 'Formato',
trackFavorite: 'Favorito',
trackRating: 'Calificación',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Género',
duration: 'Duración',
track: 'Pista',
bpm: 'BPM',
playCount: 'Reproducciones',
lastPlayed: 'Última reproducción',
format: 'Formato',
bitrate: 'Bitrate',
sampleRate: 'Frecuencia de Muestreo',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Artiste',
trackGenre: 'Genre',
trackPlayCount: 'Lectures',
trackLastPlayed: 'Dernière lecture',
trackBpm: 'BPM',
trackFormat: 'Format',
trackFavorite: 'Favori',
trackRating: 'Note',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Genre',
duration: 'Durée',
track: 'Piste',
bpm: 'BPM',
playCount: 'Nombre de lectures',
lastPlayed: 'Dernière lecture',
format: 'Format',
bitrate: 'Débit',
sampleRate: 'Fréquence d\'échantillonnage',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Artist',
trackGenre: 'Sjanger',
trackPlayCount: 'Avspillinger',
trackLastPlayed: 'Sist spilt',
trackBpm: 'BPM',
trackFormat: 'Format',
trackFavorite: 'Favoritt',
trackRating: 'Vurdering',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Sjanger',
duration: 'Varighet',
track: 'Spor',
bpm: 'BPM',
playCount: 'Avspillinger',
lastPlayed: 'Sist spilt',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Samplingfrekvens',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Artiest',
trackGenre: 'Genre',
trackPlayCount: 'Weergaven',
trackLastPlayed: 'Laatst afgespeeld',
trackBpm: 'BPM',
trackFormat: 'Formaat',
trackFavorite: 'Favoriet',
trackRating: 'Beoordeling',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Genre',
duration: 'Duur',
track: 'Track',
bpm: 'BPM',
playCount: 'Aantal weergaven',
lastPlayed: 'Laatst afgespeeld',
format: 'Formaat',
bitrate: 'Bitrate',
sampleRate: 'Samplefrequentie',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: 'Album',
trackArtist: 'Artist',
trackGenre: 'Gen',
trackPlayCount: 'Redări',
trackLastPlayed: 'Ultima redare',
trackBpm: 'BPM',
trackFormat: 'Format',
trackFavorite: 'Favorit',
trackRating: 'Rating',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Gen',
duration: 'Durată',
track: 'Piesă',
bpm: 'BPM',
playCount: 'Număr de redări',
lastPlayed: 'Ultima redare',
format: 'Format',
bitrate: 'Bitrate',
sampleRate: 'Rată de Sample',
+3
View File
@@ -28,6 +28,9 @@ export const albumDetail = {
trackAlbum: 'Альбом',
trackArtist: 'Исполнитель',
trackGenre: 'Жанр',
trackPlayCount: 'Прослушивания',
trackLastPlayed: 'Последнее воспроизведение',
trackBpm: 'BPM',
trackFormat: 'Формат',
trackFavorite: 'Избранное',
trackRating: 'Оценка',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: 'Жанр',
duration: 'Длительность',
track: 'Номер',
bpm: 'BPM',
playCount: 'Количество прослушиваний',
lastPlayed: 'Последнее воспроизведение',
format: 'Формат',
bitrate: 'Битрейт',
sampleRate: 'Частота',
+3
View File
@@ -27,6 +27,9 @@ export const albumDetail = {
trackAlbum: '专辑',
trackArtist: '艺术家',
trackGenre: '流派',
trackPlayCount: '播放次数',
trackLastPlayed: '上次播放',
trackBpm: 'BPM',
trackFormat: '格式',
trackFavorite: '收藏',
trackRating: '评分',
+3
View File
@@ -8,6 +8,9 @@ export const songInfo = {
genre: '流派',
duration: '时长',
track: '曲目',
bpm: 'BPM',
playCount: '播放次数',
lastPlayed: '上次播放',
format: '格式',
bitrate: '比特率',
sampleRate: '采样率',