fix(quality-badge): distinguish lossy formats from CD quality

MP3 and other lossy formats (aac, m4a, ogg, opus, wma) at CD-like
sample rates (44.1-48kHz) were incorrectly shown as "CD" quality
because bitDepth check came before format check.

Changes:
- Add format prop to QualityBadge, NowPlayingBar, ExpandedPlayer
- Add format field to PlayingTrack interface
- Check for lossy formats BEFORE checking bitDepth/samplingRate
- New 'mp3-cd' tier for lossy formats at CD sample rates
- Uses /mp3-cd.svg icon for this tier
This commit is contained in:
vicrodh committed 2026-01-25 05:45:01 -06:00
1 parent 5e5f85a86f
commit fa8af9b1d9
5 files changed
+36 -5

No files matched your search

+3 -1
View File
@@ -31,6 +31,7 @@
qualityLevel: number;
bitDepth?: number;
samplingRate?: number;
format?: string;
isPlaying: boolean;
onTogglePlay: () => void;
onSkipBack?: () => void;
@@ -71,6 +72,7 @@
qualityLevel,
bitDepth,
samplingRate,
format,
isPlaying,
onTogglePlay,
onSkipBack,
@@ -255,7 +257,7 @@
<h3 class="album">{album}</h3>
</div>
<div class="quality-info">
<QualityBadge {quality} {bitDepth} samplingRate={displaySamplingRate} />
<QualityBadge {quality} {bitDepth} samplingRate={displaySamplingRate} {format} />
</div>
</div>
+3 -1
View File
@@ -37,6 +37,7 @@
qualityLevel?: number;
bitDepth?: number;
samplingRate?: number;
format?: string;
isPlaying?: boolean;
onTogglePlay?: () => void;
onSkipBack?: () => void;
@@ -75,6 +76,7 @@
qualityLevel = 0,
bitDepth,
samplingRate,
format,
isPlaying = false,
onTogglePlay,
onSkipBack,
@@ -318,7 +320,7 @@
</div>
<div class="quality-indicator">
<QualityBadge {quality} {bitDepth} {samplingRate} />
<QualityBadge {quality} {bitDepth} {samplingRate} {format} />
<div class="audio-badges-row">
<AudioOutputBadges {samplingRate} />
</div>
+26 -3
View File
@@ -3,17 +3,34 @@
quality?: string;
bitDepth?: number;
samplingRate?: number;
format?: string;
}
let {
quality = '',
bitDepth,
samplingRate
samplingRate,
format
}: Props = $props();
// Define lossy formats
const LOSSY_FORMATS = ['mp3', 'aac', 'm4a', 'ogg', 'opus', 'wma'];
// Determine quality tier
const tier = $derived.by(() => {
// Check bitDepth and samplingRate first
const fmt = (format || quality || '').toLowerCase();
// Check for lossy formats FIRST - they should never be "CD" quality
const isLossyFormat = LOSSY_FORMATS.some(f => fmt.includes(f));
if (isLossyFormat) {
// MP3/lossy at CD-like sample rates gets special 'mp3-cd' tier
if (samplingRate && samplingRate >= 44.1 && samplingRate <= 48) {
return 'mp3-cd';
}
return 'lossy';
}
// Check bitDepth and samplingRate for lossless formats
if (bitDepth && bitDepth >= 24 && samplingRate && samplingRate > 96) {
return 'max';
}
@@ -24,7 +41,7 @@
return 'cd';
}
// Check quality string
// Check quality string for streaming sources
const q = quality.toLowerCase();
if (q.includes('mp3') || q.includes('320')) {
return 'lossy';
@@ -61,6 +78,10 @@
const rate = samplingRate || 44.1;
return `${depth}-bit / ${rate} kHz`;
}
if (tier === 'mp3-cd') {
const rate = samplingRate || 44.1;
return `${rate} kHz (Lossy)`;
}
if (tier === 'lossy') {
return '320 kbps';
}
@@ -71,6 +92,7 @@
if (tier === 'max') return 'Hi-Res';
if (tier === 'hires') return 'Hi-Res';
if (tier === 'cd') return 'CD';
if (tier === 'mp3-cd') return 'MP3';
if (tier === 'lossy') return 'MP3';
return 'CD';
});
@@ -79,6 +101,7 @@
const iconPath = $derived.by(() => {
if (tier === 'max' || tier === 'hires') return '/hi-res-gray.svg';
if (tier === 'cd') return '/cd.svg';
if (tier === 'mp3-cd') return '/mp3-cd.svg';
if (tier === 'lossy') return '/mp3.svg';
return '/cd.svg';
});
+1
View File
@@ -28,6 +28,7 @@ export interface PlayingTrack {
quality: string;
bitDepth?: number;
samplingRate?: number;
format?: string;
isLocal?: boolean;
// Optional IDs for recommendation tracking
albumId?: string;
+3
View File
@@ -1638,6 +1638,7 @@
quality,
bitDepth: track.bit_depth,
samplingRate: track.sample_rate ? track.sample_rate / 1000 : undefined, // Convert Hz to kHz (44100 → 44.1) - NO ROUNDING
format: track.format,
isLocal: true
}, { isLocal: true });
}
@@ -2695,6 +2696,7 @@
quality={currentTrack.quality}
bitDepth={currentTrack.bitDepth}
samplingRate={currentTrack.samplingRate}
format={currentTrack.format}
{isPlaying}
onTogglePlay={togglePlay}
onSkipBack={handleSkipBack}
@@ -2779,6 +2781,7 @@
qualityLevel={currentTrack.quality.includes('24') ? 5 : 3}
bitDepth={currentTrack.bitDepth}
samplingRate={currentTrack.samplingRate}
format={currentTrack.format}
{isPlaying}
onTogglePlay={togglePlay}
onSkipBack={handleSkipBack}