diff --git a/lib/screens/downloaded_album_screen.dart b/lib/screens/downloaded_album_screen.dart index 732e0368..33573ac4 100644 --- a/lib/screens/downloaded_album_screen.dart +++ b/lib/screens/downloaded_album_screen.dart @@ -945,35 +945,24 @@ class _DownloadedAlbumScreenState extends ConsumerState { for (final id in _selectedIds) { final item = tracksById[id]; if (item == null) continue; - final nameToCheck = - (item.safFileName != null && item.safFileName!.isNotEmpty) - ? item.safFileName!.toLowerCase() - : item.filePath.toLowerCase(); - final ext = nameToCheck.endsWith('.flac') - ? 'FLAC' - : nameToCheck.endsWith('.alac') - ? 'ALAC' - : nameToCheck.endsWith('.m4a') - ? 'M4A' - : (nameToCheck.endsWith('.aac') || nameToCheck.endsWith('.mp4a')) - ? 'AAC' - : nameToCheck.endsWith('.mp3') - ? 'MP3' - : (nameToCheck.endsWith('.opus') || nameToCheck.endsWith('.ogg')) - ? 'Opus' - : null; - if (ext != null) sourceFormats.add(ext); + final sourceFormat = convertibleAudioSourceFormat( + storedFormat: item.format, + filePath: item.filePath, + fileName: item.safFileName, + ); + if (sourceFormat != null) sourceFormats.add(sourceFormat); } - final formats = ['ALAC', 'FLAC', 'AAC', 'MP3', 'Opus'].where((target) { - return sourceFormats.any((src) { - if (src == target) return false; - final isLosslessTarget = target == 'ALAC' || target == 'FLAC'; - final isLosslessSource = src == 'FLAC' || src == 'ALAC' || src == 'M4A'; - if (isLosslessTarget && !isLosslessSource) return false; - return true; - }); - }).toList(); + final formats = audioConversionTargetFormats + .where( + (target) => sourceFormats.any( + (source) => canConvertAudioFormat( + sourceFormat: source, + targetFormat: target, + ), + ), + ) + .toList(); if (formats.isEmpty) return; @@ -1148,23 +1137,18 @@ class _DownloadedAlbumScreenState extends ConsumerState { for (final id in _selectedIds) { final item = tracksById[id]; if (item == null) continue; - final nameToCheck = - (item.safFileName != null && item.safFileName!.isNotEmpty) - ? item.safFileName!.toLowerCase() - : item.filePath.toLowerCase(); - final ext = nameToCheck.endsWith('.flac') - ? 'FLAC' - : nameToCheck.endsWith('.m4a') - ? 'M4A' - : nameToCheck.endsWith('.mp3') - ? 'MP3' - : (nameToCheck.endsWith('.opus') || nameToCheck.endsWith('.ogg')) - ? 'Opus' - : null; - if (ext == null || ext == targetFormat) continue; - final isLosslessTarget = targetFormat == 'ALAC' || targetFormat == 'FLAC'; - final isLosslessSource = ext == 'FLAC' || ext == 'M4A'; - if (isLosslessTarget && !isLosslessSource) continue; + final sourceFormat = convertibleAudioSourceFormat( + storedFormat: item.format, + filePath: item.filePath, + fileName: item.safFileName, + ); + if (sourceFormat == null || + !canConvertAudioFormat( + sourceFormat: sourceFormat, + targetFormat: targetFormat, + )) { + continue; + } selected.add(item); } diff --git a/test/models_and_utils_test.dart b/test/models_and_utils_test.dart index 9b4cf3a5..4d929acb 100644 --- a/test/models_and_utils_test.dart +++ b/test/models_and_utils_test.dart @@ -7,6 +7,7 @@ import 'package:spotiflac_android/models/track.dart'; import 'package:spotiflac_android/services/app_remote_config_service.dart'; import 'package:spotiflac_android/services/download_request_payload.dart'; import 'package:spotiflac_android/utils/artist_utils.dart'; +import 'package:spotiflac_android/utils/audio_conversion_utils.dart'; import 'package:spotiflac_android/utils/mime_utils.dart'; import 'package:spotiflac_android/utils/path_match_keys.dart'; import 'package:spotiflac_android/utils/string_utils.dart'; @@ -426,6 +427,49 @@ void main() { }); }); + group('audio conversion utils', () { + test('detects Dolby formats from stored scan format before file extension', () { + expect( + convertibleAudioSourceFormat( + storedFormat: 'eac3', + filePath: 'content://media/song.m4a', + ), + 'EAC3', + ); + expect( + convertibleAudioSourceFormat(fileName: 'Song.ac-3'), + 'AC3', + ); + expect( + convertibleAudioSourceFormat(storedFormat: 'ac4'), + 'AC4', + ); + }); + + test('allows Dolby sources only for lossy batch conversion targets', () { + expect( + canConvertAudioFormat(sourceFormat: 'EAC3', targetFormat: 'MP3'), + isTrue, + ); + expect( + canConvertAudioFormat(sourceFormat: 'EAC3', targetFormat: 'Opus'), + isTrue, + ); + expect( + canConvertAudioFormat(sourceFormat: 'EAC3', targetFormat: 'AAC'), + isTrue, + ); + expect( + canConvertAudioFormat(sourceFormat: 'EAC3', targetFormat: 'FLAC'), + isFalse, + ); + expect( + canConvertAudioFormat(sourceFormat: 'EAC3', targetFormat: 'ALAC'), + isFalse, + ); + }); + }); + group('string utils', () { test('normalizes optional strings and cover references', () { expect(normalizeOptionalString(null), isNull);