mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-16 23:05:31 +02:00
fix(library): retain Opus badges when bitrate is missing
This commit is contained in:
@@ -110,6 +110,8 @@ class UnifiedLibraryItem {
|
||||
return buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: history.format,
|
||||
filePath: history.filePath,
|
||||
fileName: history.safFileName,
|
||||
bitrateKbps: history.bitrate,
|
||||
bitDepth: history.bitDepth,
|
||||
sampleRate: history.sampleRate,
|
||||
@@ -122,6 +124,7 @@ class UnifiedLibraryItem {
|
||||
return buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: local.format,
|
||||
filePath: local.filePath,
|
||||
bitrateKbps: local.bitrate,
|
||||
bitDepth: local.bitDepth,
|
||||
sampleRate: local.sampleRate,
|
||||
|
||||
@@ -495,6 +495,8 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen>
|
||||
String? label(DownloadHistoryItem track) => buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: track.format,
|
||||
filePath: track.filePath,
|
||||
fileName: track.safFileName,
|
||||
bitrateKbps: track.bitrate,
|
||||
bitDepth: track.bitDepth,
|
||||
sampleRate: track.sampleRate,
|
||||
|
||||
@@ -323,6 +323,7 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen>
|
||||
String? label(LocalLibraryItem track) => buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: track.format,
|
||||
filePath: track.filePath,
|
||||
bitrateKbps: track.bitrate,
|
||||
bitDepth: track.bitDepth,
|
||||
sampleRate: track.sampleRate,
|
||||
|
||||
@@ -49,11 +49,16 @@ String? _libraryFileFormatLabel(String? format) {
|
||||
String? buildLibraryAudioQualityLabel({
|
||||
required String mode,
|
||||
String? format,
|
||||
String? filePath,
|
||||
String? fileName,
|
||||
int? bitrateKbps,
|
||||
int? bitDepth,
|
||||
int? sampleRate,
|
||||
String? storedQuality,
|
||||
}) {
|
||||
final effectiveFormat =
|
||||
normalizeOptionalString(format) ??
|
||||
audioFormatForPath(filePath, fileName: fileName);
|
||||
final stored = normalizeOptionalString(storedQuality);
|
||||
final storedBitrate = _bitrateFromStoredQuality(stored);
|
||||
final storedBitDepth = _bitDepthFromStoredQuality(stored);
|
||||
@@ -64,7 +69,10 @@ String? buildLibraryAudioQualityLabel({
|
||||
? bitDepth
|
||||
: storedBitDepth;
|
||||
final bitrateLabel = effectiveBitrate != null
|
||||
? buildDisplayAudioQuality(bitrateKbps: effectiveBitrate, format: format)
|
||||
? buildDisplayAudioQuality(
|
||||
bitrateKbps: effectiveBitrate,
|
||||
format: effectiveFormat,
|
||||
)
|
||||
: null;
|
||||
final bitDepthLabel =
|
||||
bitDepth != null && bitDepth > 0 && sampleRate != null && sampleRate > 0
|
||||
@@ -76,11 +84,14 @@ String? buildLibraryAudioQualityLabel({
|
||||
final normalizedMode = normalizeLibraryQualityLabelMode(mode);
|
||||
|
||||
if (normalizedMode == AppSettings.libraryQualityLabelFileFormat) {
|
||||
return _libraryFileFormatLabel(format);
|
||||
return _libraryFileFormatLabel(effectiveFormat);
|
||||
}
|
||||
|
||||
if (isLossyAudioFormat(format)) {
|
||||
return bitrateLabel;
|
||||
if (isLossyAudioFormat(effectiveFormat)) {
|
||||
// Older rows may know the codec before their bitrate has been backfilled.
|
||||
// Keep a useful badge without showing stale lossless specs or inventing a
|
||||
// bitrate from the user's current download/conversion setting.
|
||||
return bitrateLabel ?? _libraryFileFormatLabel(effectiveFormat);
|
||||
}
|
||||
|
||||
return switch (normalizedMode) {
|
||||
|
||||
@@ -1,8 +1,50 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:spotiflac_android/models/settings.dart';
|
||||
import 'package:spotiflac_android/models/unified_library_item.dart';
|
||||
import 'package:spotiflac_android/providers/download_history_provider.dart';
|
||||
import 'package:spotiflac_android/services/library_database.dart';
|
||||
import 'package:spotiflac_android/utils/audio_quality_badge_policy.dart';
|
||||
|
||||
void main() {
|
||||
test(
|
||||
'Library history and local cards retain labels for legacy Opus rows',
|
||||
() {
|
||||
final history = UnifiedLibraryItem.fromDownloadHistory(
|
||||
DownloadHistoryItem(
|
||||
id: 'history-song',
|
||||
trackName: 'Song',
|
||||
artistName: 'Artist',
|
||||
albumName: 'Album',
|
||||
filePath: 'content://music/document/42',
|
||||
safFileName: 'Song.opus',
|
||||
quality: '16-bit/44.1kHz',
|
||||
service: 'example',
|
||||
downloadedAt: DateTime(2026),
|
||||
),
|
||||
);
|
||||
final local = UnifiedLibraryItem.fromLocalLibrary(
|
||||
LocalLibraryItem(
|
||||
id: 'local-song',
|
||||
trackName: 'Song',
|
||||
artistName: 'Artist',
|
||||
albumName: 'Album',
|
||||
filePath: '/music/Song.opus',
|
||||
scannedAt: DateTime(2026),
|
||||
),
|
||||
);
|
||||
for (final item in [history, local]) {
|
||||
expect(
|
||||
item.qualityForMode(AppSettings.libraryQualityLabelBitDepth),
|
||||
'OPUS',
|
||||
);
|
||||
expect(
|
||||
item.qualityForMode(AppSettings.libraryQualityLabelBitrate),
|
||||
'OPUS',
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
group('Library audio quality badge color', () {
|
||||
test('keeps legacy 24-bit labels highlighted', () {
|
||||
expect(shouldHighlightAudioQualityBadge('24-bit/96kHz'), isTrue);
|
||||
@@ -25,6 +67,77 @@ void main() {
|
||||
});
|
||||
|
||||
group('Library audio quality label mode', () {
|
||||
test('keeps an Opus badge when bitrate has not been backfilled', () {
|
||||
for (final mode in [
|
||||
AppSettings.libraryQualityLabelBitrate,
|
||||
AppSettings.libraryQualityLabelBitDepth,
|
||||
AppSettings.libraryQualityLabelBitDepthOnly,
|
||||
AppSettings.libraryQualityLabelBitDepthBitrate,
|
||||
AppSettings.libraryQualityLabelFileFormat,
|
||||
]) {
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: mode,
|
||||
format: 'opus',
|
||||
bitDepth: 16,
|
||||
sampleRate: 44100,
|
||||
storedQuality: '16-bit/44.1kHz',
|
||||
),
|
||||
'OPUS',
|
||||
reason: mode,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('uses stored Opus bitrate until measured bitrate is available', () {
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: AppSettings.libraryQualityLabelBitDepth,
|
||||
format: 'opus',
|
||||
storedQuality: 'OPUS 320kbps',
|
||||
),
|
||||
'OPUS 320kbps',
|
||||
);
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: AppSettings.libraryQualityLabelBitDepth,
|
||||
format: 'opus',
|
||||
bitrateKbps: 256,
|
||||
storedQuality: 'OPUS 320kbps',
|
||||
),
|
||||
'OPUS 256kbps',
|
||||
);
|
||||
expect(formatLibraryGridAudioQualityLabel('OPUS 320kbps'), '320k');
|
||||
expect(formatLibraryGridAudioQualityLabel('OPUS'), 'OPUS');
|
||||
});
|
||||
|
||||
test('uses the path or SAF name when a legacy row has no format', () {
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: AppSettings.libraryQualityLabelBitDepth,
|
||||
filePath: '/music/Song.opus',
|
||||
storedQuality: '16-bit/44.1kHz',
|
||||
),
|
||||
'OPUS',
|
||||
);
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: AppSettings.libraryQualityLabelBitrate,
|
||||
filePath: 'content://music/document/42',
|
||||
fileName: 'Song.OPUS',
|
||||
),
|
||||
'OPUS',
|
||||
);
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
mode: AppSettings.libraryQualityLabelFileFormat,
|
||||
format: 'alac',
|
||||
filePath: '/music/Song.m4a',
|
||||
),
|
||||
'ALAC',
|
||||
);
|
||||
});
|
||||
|
||||
test('uses measured bitrate by default', () {
|
||||
expect(
|
||||
buildLibraryAudioQualityLabel(
|
||||
|
||||
Reference in New Issue
Block a user