feat(tidal): add native AAC 320kbps quality option

- Add HIGH quality option (AAC 320kbps) for Tidal downloads
- Download directly as M4A without FLAC conversion
- Embed metadata to M4A using EmbedM4AMetadata()
- Skip M4A to FLAC conversion in download provider for HIGH quality
- Add AAC 320kbps option in settings page (Tidal only)
- Add HIGH quality option in download service picker
This commit is contained in:
zarzet
2026-02-01 17:26:25 +07:00
parent 9d479b61d6
commit 2073516666
4 changed files with 87 additions and 16 deletions
+9 -3
View File
@@ -1980,9 +1980,14 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
}
if (filePath != null && filePath.endsWith('.m4a')) {
_log.d(
'M4A file detected (Hi-Res DASH stream), attempting conversion to FLAC...',
);
// For HIGH quality (native AAC 320kbps), skip M4A to FLAC conversion
if (quality == 'HIGH') {
_log.i('Native AAC 320kbps download (HIGH quality), keeping M4A file');
actualQuality = 'AAC 320kbps';
} else {
_log.d(
'M4A file detected (Hi-Res DASH stream), attempting conversion to FLAC...',
);
try {
final file = File(filePath);
@@ -2084,6 +2089,7 @@ class DownloadQueueNotifier extends Notifier<DownloadQueueState> {
} catch (e) {
_log.w('FFmpeg conversion process failed: $e, keeping M4A file');
}
} // end else (not HIGH quality)
}
final itemAfterDownload = state.items.firstWhere(
@@ -94,6 +94,7 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
final topPadding = MediaQuery.of(context).padding.top;
final isBuiltInService = _builtInServices.contains(settings.defaultService);
final isTidalService = settings.defaultService == 'tidal';
return PopScope(
canPop: true,
@@ -215,8 +216,19 @@ class _DownloadSettingsPageState extends ConsumerState<DownloadSettingsPage> {
onTap: () => ref
.read(settingsProvider.notifier)
.setAudioQuality('HI_RES_LOSSLESS'),
showDivider: settings.enableLossyOption,
showDivider: isTidalService || settings.enableLossyOption,
),
// Native AAC 320kbps option (Tidal only)
if (isTidalService)
_QualityOption(
title: 'AAC 320kbps',
subtitle: 'Native AAC (no conversion)',
isSelected: settings.audioQuality == 'HIGH',
onTap: () => ref
.read(settingsProvider.notifier)
.setAudioQuality('HIGH'),
showDivider: settings.enableLossyOption,
),
if (settings.enableLossyOption)
_QualityOption(
title: context.l10n.qualityLossy,
+3
View File
@@ -27,6 +27,7 @@ const _builtInServices = [
QualityOption(id: 'LOSSLESS', label: 'FLAC Lossless', description: '16-bit / 44.1kHz'),
QualityOption(id: 'HI_RES', label: 'Hi-Res FLAC', description: '24-bit / up to 96kHz'),
QualityOption(id: 'HI_RES_LOSSLESS', label: 'Hi-Res FLAC Max', description: '24-bit / up to 192kHz'),
QualityOption(id: 'HIGH', label: 'AAC 320kbps', description: 'Native AAC (no conversion)'),
],
),
BuiltInService(
@@ -257,6 +258,8 @@ class _DownloadServicePickerState extends ConsumerState<DownloadServicePicker> {
return Icons.high_quality;
case 'LOSSLESS':
return Icons.music_note;
case 'HIGH':
return Icons.aod;
case 'MP3_320':
case 'MP3':
case 'LOSSY':