From 572eb81927792d6e853eeb4387490e221d97236d Mon Sep 17 00:00:00 2001 From: Maxim Isaev Date: Thu, 16 Apr 2026 23:15:13 +0300 Subject: [PATCH] feat(queue): show ReplayGain metadata in current-track tech strip Display track gain, album gain, and peak from file tags next to format, bitrate, and sample rate so missing ReplayGain tags are obvious. Refs: https://github.com/Psychotoxical/psysonic/issues/195 --- src/components/QueuePanel.tsx | 49 +++++++++++++++++++++++------------ src/locales/de.ts | 3 +++ src/locales/en.ts | 3 +++ src/locales/es.ts | 3 +++ src/locales/fr.ts | 3 +++ src/locales/nb.ts | 3 +++ src/locales/nl.ts | 3 +++ src/locales/ru.ts | 3 +++ src/locales/zh.ts | 3 +++ 9 files changed, 57 insertions(+), 16 deletions(-) diff --git a/src/components/QueuePanel.tsx b/src/components/QueuePanel.tsx index 79de75a9..af6d9380 100644 --- a/src/components/QueuePanel.tsx +++ b/src/components/QueuePanel.tsx @@ -20,6 +20,21 @@ function formatTime(seconds: number): string { return `${m}:${s.toString().padStart(2, '0')}`; } +function formatQueueReplayGainParts(track: Track, t: TFunction): string[] { + const parts: string[] = []; + const fmtDb = (db: number) => `${db >= 0 ? '+' : ''}${db.toFixed(1)}`; + if (track.replayGainTrackDb != null) { + parts.push(t('queue.rgTrack', { db: fmtDb(track.replayGainTrackDb) })); + } + if (track.replayGainAlbumDb != null) { + parts.push(t('queue.rgAlbum', { db: fmtDb(track.replayGainAlbumDb) })); + } + if (track.replayGainPeak != null) { + parts.push(t('queue.rgPeak', { pk: track.replayGainPeak.toFixed(3) })); + } + return parts; +} + function renderStars(rating?: number) { if (!rating) return null; const stars = []; @@ -420,22 +435,24 @@ export default function QueuePanel() { {currentTrack && (
- {(currentTrack.suffix || currentTrack.bitRate || currentTrack.samplingRate || currentTrack.bitDepth) && ( -
- {[ - currentTrack.suffix?.toUpperCase(), - currentTrack.bitRate ? `${currentTrack.bitRate} kbps` : undefined, - (() => { - const bd = currentTrack.bitDepth; - const sr = currentTrack.samplingRate ? `${currentTrack.samplingRate / 1000} kHz` : ''; - if (bd && sr) return `${bd}/${sr}`; - if (bd) return `${bd}-bit`; - if (sr) return sr; - return undefined; - })(), - ].filter(Boolean).join(' · ')} -
- )} + {(() => { + const baseParts = [ + currentTrack.suffix?.toUpperCase(), + currentTrack.bitRate ? `${currentTrack.bitRate} kbps` : undefined, + (() => { + const bd = currentTrack.bitDepth; + const sr = currentTrack.samplingRate ? `${currentTrack.samplingRate / 1000} kHz` : ''; + if (bd && sr) return `${bd}/${sr}`; + if (bd) return `${bd}-bit`; + if (sr) return sr; + return undefined; + })(), + ].filter(Boolean) as string[]; + const rgParts = formatQueueReplayGainParts(currentTrack, t); + const techLine = [...baseParts, ...rgParts].join(' · '); + if (!techLine) return null; + return
{techLine}
; + })()}
{currentTrack.coverArt ? ( diff --git a/src/locales/de.ts b/src/locales/de.ts index f30c364b..49ac32ff 100644 --- a/src/locales/de.ts +++ b/src/locales/de.ts @@ -850,6 +850,9 @@ export const deTranslation = { trackPlural: 'Titel', showRemaining: 'Restzeit anzeigen', showTotal: 'Gesamtzeit anzeigen', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Peak {{pk}}', }, statistics: { title: 'Statistiken', diff --git a/src/locales/en.ts b/src/locales/en.ts index 9fc35ab8..911051e9 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -852,6 +852,9 @@ export const enTranslation = { trackPlural: 'tracks', showRemaining: 'Show remaining time', showTotal: 'Show total time', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Peak {{pk}}', }, statistics: { title: 'Statistics', diff --git a/src/locales/es.ts b/src/locales/es.ts index 7f8a6e01..57e13316 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -853,6 +853,9 @@ export const esTranslation = { trackPlural: 'pistas', showRemaining: 'Mostrar tiempo restante', showTotal: 'Mostrar tiempo total', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Pico {{pk}}', }, statistics: { title: 'Estadísticas', diff --git a/src/locales/fr.ts b/src/locales/fr.ts index d100007b..fb2269b5 100644 --- a/src/locales/fr.ts +++ b/src/locales/fr.ts @@ -848,6 +848,9 @@ export const frTranslation = { trackPlural: 'pistes', showRemaining: 'Afficher le temps restant', showTotal: 'Afficher la durée totale', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Pic {{pk}}', }, statistics: { title: 'Statistiques', diff --git a/src/locales/nb.ts b/src/locales/nb.ts index fedf1f7d..3049d001 100644 --- a/src/locales/nb.ts +++ b/src/locales/nb.ts @@ -847,6 +847,9 @@ export const nbTranslation = { trackPlural: 'spor', showRemaining: 'Vis gjenværende tid', showTotal: 'Vis total tid', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Topp {{pk}}', }, statistics: { title: 'Statistikk', diff --git a/src/locales/nl.ts b/src/locales/nl.ts index eba6b6b6..2f93e489 100644 --- a/src/locales/nl.ts +++ b/src/locales/nl.ts @@ -847,6 +847,9 @@ export const nlTranslation = { trackPlural: 'nummers', showRemaining: 'Resterende tijd tonen', showTotal: 'Totale tijd tonen', + rgTrack: 'T {{db}} dB', + rgAlbum: 'A {{db}} dB', + rgPeak: 'Piek {{pk}}', }, statistics: { title: 'Statistieken', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 55caca37..830469ed 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -898,6 +898,9 @@ export const ruTranslation = { trackPlural: 'треков', showRemaining: 'Осталось', showTotal: 'Всего', + rgTrack: 'Т {{db}} дБ', + rgAlbum: 'А {{db}} дБ', + rgPeak: 'Пик {{pk}}', }, statistics: { title: 'Статистика', diff --git a/src/locales/zh.ts b/src/locales/zh.ts index aa26d7c9..bc416374 100644 --- a/src/locales/zh.ts +++ b/src/locales/zh.ts @@ -843,6 +843,9 @@ export const zhTranslation = { trackPlural: '首曲目', showRemaining: '显示剩余时间', showTotal: '显示总时间', + rgTrack: '曲目 {{db}} dB', + rgAlbum: '专辑 {{db}} dB', + rgPeak: '峰值 {{pk}}', }, statistics: { title: '统计',