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:
1 parent
5e5f85a86f
commit
fa8af9b1d9
5 files changed
+36
-5
No files matched your search
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface PlayingTrack {
|
||||
quality: string;
|
||||
bitDepth?: number;
|
||||
samplingRate?: number;
|
||||
format?: string;
|
||||
isLocal?: boolean;
|
||||
// Optional IDs for recommendation tracking
|
||||
albumId?: string;
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user