From cecb687592b427045ec9c2de5f4f8d7d0429c1a9 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Fri, 19 Sep 2025 11:53:36 +0600 Subject: [PATCH] feat(playback): add uncompressed flac playback support --- cli/commands/install-dependencies.dart | 5 ++ lib/l10n/app_en.arb | 3 +- lib/l10n/generated/app_localizations.dart | 6 ++ lib/l10n/generated/app_localizations_ar.dart | 3 + lib/l10n/generated/app_localizations_bn.dart | 3 + lib/l10n/generated/app_localizations_ca.dart | 3 + lib/l10n/generated/app_localizations_cs.dart | 3 + lib/l10n/generated/app_localizations_de.dart | 3 + lib/l10n/generated/app_localizations_en.dart | 3 + lib/l10n/generated/app_localizations_es.dart | 3 + lib/l10n/generated/app_localizations_eu.dart | 3 + lib/l10n/generated/app_localizations_fa.dart | 3 + lib/l10n/generated/app_localizations_fi.dart | 3 + lib/l10n/generated/app_localizations_fr.dart | 3 + lib/l10n/generated/app_localizations_hi.dart | 3 + lib/l10n/generated/app_localizations_id.dart | 3 + lib/l10n/generated/app_localizations_it.dart | 3 + lib/l10n/generated/app_localizations_ja.dart | 3 + lib/l10n/generated/app_localizations_ka.dart | 3 + lib/l10n/generated/app_localizations_ko.dart | 3 + lib/l10n/generated/app_localizations_ne.dart | 3 + lib/l10n/generated/app_localizations_nl.dart | 3 + lib/l10n/generated/app_localizations_pl.dart | 3 + lib/l10n/generated/app_localizations_pt.dart | 3 + lib/l10n/generated/app_localizations_ru.dart | 3 + lib/l10n/generated/app_localizations_ta.dart | 3 + lib/l10n/generated/app_localizations_th.dart | 3 + lib/l10n/generated/app_localizations_tl.dart | 3 + lib/l10n/generated/app_localizations_tr.dart | 3 + lib/l10n/generated/app_localizations_uk.dart | 3 + lib/l10n/generated/app_localizations_vi.dart | 3 + lib/l10n/generated/app_localizations_zh.dart | 3 + lib/models/database/tables/preferences.dart | 8 -- lib/pages/settings/sections/playback.dart | 5 ++ lib/provider/server/routes/playback.dart | 22 +++-- .../user_preferences_provider.dart | 25 ++++++ .../audio_player/audio_player_impl.dart | 4 + lib/services/audio_player/custom_player.dart | 8 ++ lib/services/sourced_track/enums.dart | 4 +- lib/services/sourced_track/sourced_track.dart | 5 +- .../sourced_track/sources/dab_music.dart | 13 +-- untranslated_messages.json | 87 ++++++++++++------- 42 files changed, 232 insertions(+), 50 deletions(-) diff --git a/cli/commands/install-dependencies.dart b/cli/commands/install-dependencies.dart index e26b8078..336ffae7 100644 --- a/cli/commands/install-dependencies.dart +++ b/cli/commands/install-dependencies.dart @@ -39,6 +39,11 @@ class InstallDependenciesCommand extends Command { switch (argResults!.option("platform")) { case "windows": + await shell.run( + """ + choco install innosetup -y + """, + ); break; case "linux": await shell.run( diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index de4a784b..228e24b1 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -462,5 +462,6 @@ "configure_your_own_metadata_plugin": "Configure your own playlist/album/artist/feed metadata provider", "audio_scrobblers": "Audio Scrobblers", "scrobbling": "Scrobbling", - "source": "Source: " + "source": "Source: ", + "uncompressed": "Uncompressed" } diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 79534e94..b2c94dc1 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -2936,6 +2936,12 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Source: '** String get source; + + /// No description provided for @uncompressed. + /// + /// In en, this message translates to: + /// **'Uncompressed'** + String get uncompressed; } class _AppLocalizationsDelegate diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart index e3d72f81..cd2bc7e9 100644 --- a/lib/l10n/generated/app_localizations_ar.dart +++ b/lib/l10n/generated/app_localizations_ar.dart @@ -1540,4 +1540,7 @@ class AppLocalizationsAr extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_bn.dart b/lib/l10n/generated/app_localizations_bn.dart index cfa4d31a..2246c64c 100644 --- a/lib/l10n/generated/app_localizations_bn.dart +++ b/lib/l10n/generated/app_localizations_bn.dart @@ -1541,4 +1541,7 @@ class AppLocalizationsBn extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ca.dart b/lib/l10n/generated/app_localizations_ca.dart index 458286dd..c0d55c75 100644 --- a/lib/l10n/generated/app_localizations_ca.dart +++ b/lib/l10n/generated/app_localizations_ca.dart @@ -1551,4 +1551,7 @@ class AppLocalizationsCa extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_cs.dart b/lib/l10n/generated/app_localizations_cs.dart index 6c89a2ab..2c1e4dfa 100644 --- a/lib/l10n/generated/app_localizations_cs.dart +++ b/lib/l10n/generated/app_localizations_cs.dart @@ -1541,4 +1541,7 @@ class AppLocalizationsCs extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 1a79979c..12ce5e81 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -1553,4 +1553,7 @@ class AppLocalizationsDe extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index a9125e56..fdafac30 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -1539,4 +1539,7 @@ class AppLocalizationsEn extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart index ffa6eced..b9d2bb5a 100644 --- a/lib/l10n/generated/app_localizations_es.dart +++ b/lib/l10n/generated/app_localizations_es.dart @@ -1554,4 +1554,7 @@ class AppLocalizationsEs extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_eu.dart b/lib/l10n/generated/app_localizations_eu.dart index b0a7be9a..0ac13ddc 100644 --- a/lib/l10n/generated/app_localizations_eu.dart +++ b/lib/l10n/generated/app_localizations_eu.dart @@ -1551,4 +1551,7 @@ class AppLocalizationsEu extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_fa.dart b/lib/l10n/generated/app_localizations_fa.dart index 64f797f3..140077c2 100644 --- a/lib/l10n/generated/app_localizations_fa.dart +++ b/lib/l10n/generated/app_localizations_fa.dart @@ -1539,4 +1539,7 @@ class AppLocalizationsFa extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_fi.dart b/lib/l10n/generated/app_localizations_fi.dart index 6f00104a..b1d13d8e 100644 --- a/lib/l10n/generated/app_localizations_fi.dart +++ b/lib/l10n/generated/app_localizations_fi.dart @@ -1539,4 +1539,7 @@ class AppLocalizationsFi extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart index 067e812d..6a7e51cb 100644 --- a/lib/l10n/generated/app_localizations_fr.dart +++ b/lib/l10n/generated/app_localizations_fr.dart @@ -1559,4 +1559,7 @@ class AppLocalizationsFr extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart index 0ca1641f..1be49d24 100644 --- a/lib/l10n/generated/app_localizations_hi.dart +++ b/lib/l10n/generated/app_localizations_hi.dart @@ -1545,4 +1545,7 @@ class AppLocalizationsHi extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_id.dart b/lib/l10n/generated/app_localizations_id.dart index 5545f306..225fb9a5 100644 --- a/lib/l10n/generated/app_localizations_id.dart +++ b/lib/l10n/generated/app_localizations_id.dart @@ -1547,4 +1547,7 @@ class AppLocalizationsId extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart index b130f9b4..88128c6c 100644 --- a/lib/l10n/generated/app_localizations_it.dart +++ b/lib/l10n/generated/app_localizations_it.dart @@ -1546,4 +1546,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ja.dart b/lib/l10n/generated/app_localizations_ja.dart index 104cf201..f444e4e4 100644 --- a/lib/l10n/generated/app_localizations_ja.dart +++ b/lib/l10n/generated/app_localizations_ja.dart @@ -1510,4 +1510,7 @@ class AppLocalizationsJa extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ka.dart b/lib/l10n/generated/app_localizations_ka.dart index e44e9aa7..a9efb949 100644 --- a/lib/l10n/generated/app_localizations_ka.dart +++ b/lib/l10n/generated/app_localizations_ka.dart @@ -1548,4 +1548,7 @@ class AppLocalizationsKa extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ko.dart b/lib/l10n/generated/app_localizations_ko.dart index aac88524..c6cc39c8 100644 --- a/lib/l10n/generated/app_localizations_ko.dart +++ b/lib/l10n/generated/app_localizations_ko.dart @@ -1514,4 +1514,7 @@ class AppLocalizationsKo extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ne.dart b/lib/l10n/generated/app_localizations_ne.dart index 726a8eee..6bd1214e 100644 --- a/lib/l10n/generated/app_localizations_ne.dart +++ b/lib/l10n/generated/app_localizations_ne.dart @@ -1551,4 +1551,7 @@ class AppLocalizationsNe extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_nl.dart b/lib/l10n/generated/app_localizations_nl.dart index d0a64253..e7d6ee40 100644 --- a/lib/l10n/generated/app_localizations_nl.dart +++ b/lib/l10n/generated/app_localizations_nl.dart @@ -1545,4 +1545,7 @@ class AppLocalizationsNl extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_pl.dart b/lib/l10n/generated/app_localizations_pl.dart index 8d29354e..fe0493c0 100644 --- a/lib/l10n/generated/app_localizations_pl.dart +++ b/lib/l10n/generated/app_localizations_pl.dart @@ -1547,4 +1547,7 @@ class AppLocalizationsPl extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart index 10a036af..082817ae 100644 --- a/lib/l10n/generated/app_localizations_pt.dart +++ b/lib/l10n/generated/app_localizations_pt.dart @@ -1544,4 +1544,7 @@ class AppLocalizationsPt extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart index d0d6903d..6645d9ff 100644 --- a/lib/l10n/generated/app_localizations_ru.dart +++ b/lib/l10n/generated/app_localizations_ru.dart @@ -1547,4 +1547,7 @@ class AppLocalizationsRu extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_ta.dart b/lib/l10n/generated/app_localizations_ta.dart index 20f89003..98cff256 100644 --- a/lib/l10n/generated/app_localizations_ta.dart +++ b/lib/l10n/generated/app_localizations_ta.dart @@ -1553,4 +1553,7 @@ class AppLocalizationsTa extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_th.dart b/lib/l10n/generated/app_localizations_th.dart index 98ae0ad4..0156180e 100644 --- a/lib/l10n/generated/app_localizations_th.dart +++ b/lib/l10n/generated/app_localizations_th.dart @@ -1536,4 +1536,7 @@ class AppLocalizationsTh extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_tl.dart b/lib/l10n/generated/app_localizations_tl.dart index 0b9b79d5..ddecd3cf 100644 --- a/lib/l10n/generated/app_localizations_tl.dart +++ b/lib/l10n/generated/app_localizations_tl.dart @@ -1554,4 +1554,7 @@ class AppLocalizationsTl extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart index e5a36ba1..6e9e36f2 100644 --- a/lib/l10n/generated/app_localizations_tr.dart +++ b/lib/l10n/generated/app_localizations_tr.dart @@ -1547,4 +1547,7 @@ class AppLocalizationsTr extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_uk.dart b/lib/l10n/generated/app_localizations_uk.dart index f0254780..6d8e44b1 100644 --- a/lib/l10n/generated/app_localizations_uk.dart +++ b/lib/l10n/generated/app_localizations_uk.dart @@ -1543,4 +1543,7 @@ class AppLocalizationsUk extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_vi.dart b/lib/l10n/generated/app_localizations_vi.dart index aa73fbdc..fcf545ae 100644 --- a/lib/l10n/generated/app_localizations_vi.dart +++ b/lib/l10n/generated/app_localizations_vi.dart @@ -1549,4 +1549,7 @@ class AppLocalizationsVi extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart index d3b25f71..ec8a4e4b 100644 --- a/lib/l10n/generated/app_localizations_zh.dart +++ b/lib/l10n/generated/app_localizations_zh.dart @@ -1503,6 +1503,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get source => 'Source: '; + + @override + String get uncompressed => 'Uncompressed'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). diff --git a/lib/models/database/tables/preferences.dart b/lib/models/database/tables/preferences.dart index 82c90c55..64580330 100644 --- a/lib/models/database/tables/preferences.dart +++ b/lib/models/database/tables/preferences.dart @@ -41,14 +41,6 @@ enum YoutubeClientEngine { } } -enum MusicCodec { - m4a._("M4a (Best for downloaded music)"), - weba._("WebA (Best for streamed music)\nDoesn't support audio metadata"); - - final String label; - const MusicCodec._(this.label); -} - enum SearchMode { youtube._("YouTube"), youtubeMusic._("YouTube Music"); diff --git a/lib/pages/settings/sections/playback.dart b/lib/pages/settings/sections/playback.dart index 2e1ddd27..2d128742 100644 --- a/lib/pages/settings/sections/playback.dart +++ b/lib/pages/settings/sections/playback.dart @@ -44,6 +44,11 @@ class SettingsPlaybackSection extends HookConsumerWidget { title: Text(context.l10n.audio_quality), value: preferences.audioQuality, options: [ + if (preferences.audioSource == AudioSource.dabMusic) + SelectItemButton( + value: SourceQualities.uncompressed, + child: Text(context.l10n.uncompressed), + ), SelectItemButton( value: SourceQualities.high, child: Text(context.l10n.high), diff --git a/lib/provider/server/routes/playback.dart b/lib/provider/server/routes/playback.dart index 9e1d96a9..ae51b080 100644 --- a/lib/provider/server/routes/playback.dart +++ b/lib/provider/server/routes/playback.dart @@ -101,10 +101,7 @@ class ServerPlaybackRoutes { ); final contentLengthRes = await Future.value( - dio.head( - url, - options: options, - ), + dio.head(url, options: options), ).catchError((e, stack) async { AppLogger.reportError(e, stack); @@ -135,6 +132,21 @@ class ServerPlaybackRoutes { ); } + if (headers["range"] == "bytes=0-") { + final bufferSize = + userPreferences.audioQuality == SourceQualities.uncompressed + ? 6 * 1024 * 1024 + : 4 * 1024 * 1024; + final endRange = min(bufferSize, + int.parse(contentLengthRes?.headers.value("content-length") ?? "0")); + options = options.copyWith( + headers: { + ...options.headers ?? {}, + "range": "bytes=0-$endRange", + }, + ); + } + final res = await dio.get(url, options: options); final bytes = res.data; @@ -166,7 +178,7 @@ class ServerPlaybackRoutes { await trackPartialCacheFile.rename(trackCacheFile.path); } - if (contentRange.total == fileLength && track.codec == SourceCodecs.m4a) { + if (contentRange.total == fileLength && track.codec != SourceCodecs.weba) { final playlistTrack = playlist.tracks.firstWhereOrNull( (element) => element.id == track.query.id, ); diff --git a/lib/provider/user_preferences/user_preferences_provider.dart b/lib/provider/user_preferences/user_preferences_provider.dart index a5be97e2..8e8da561 100644 --- a/lib/provider/user_preferences/user_preferences_provider.dart +++ b/lib/provider/user_preferences/user_preferences_provider.dart @@ -54,6 +54,7 @@ class UserPreferencesNotifier extends Notifier { } await audioPlayer.setAudioNormalization(state.normalizeAudio); + await _updatePlayerBufferSize(event.audioQuality, state.audioQuality); } catch (e, stack) { AppLogger.reportError(e, stack); } @@ -79,6 +80,24 @@ class UserPreferencesNotifier extends Notifier { }); } + /// Sets audio player's buffer size based on the selected audio quality + /// Uncompressed quality gets a larger buffer size for smoother playback + /// while other qualities use a standard buffer size. + Future _updatePlayerBufferSize( + SourceQualities newQuality, + SourceQualities oldQuality, + ) async { + if (newQuality == SourceQualities.uncompressed) { + audioPlayer.setDemuxerBufferSize(6 * 1024 * 1024); // 6MB + return; + } + + if (oldQuality == SourceQualities.uncompressed && + newQuality != SourceQualities.uncompressed) { + audioPlayer.setDemuxerBufferSize(4 * 1024 * 1024); // 4MB + } + } + Future setData(PreferencesTableCompanion data) async { final db = ref.read(databaseProvider); @@ -155,6 +174,7 @@ class UserPreferencesNotifier extends Notifier { void setAudioQuality(SourceQualities quality) { setData(PreferencesTableCompanion(audioQuality: Value(quality))); + _updatePlayerBufferSize(quality, state.audioQuality); } void setDownloadLocation(String downloadDir) { @@ -204,6 +224,11 @@ class UserPreferencesNotifier extends Notifier { } void setAudioSource(AudioSource type) { + // Only allow uncompressed quality for DAB Music + if (type != AudioSource.dabMusic && + state.audioQuality == SourceQualities.uncompressed) { + setAudioQuality(SourceQualities.high); + } setData(PreferencesTableCompanion(audioSource: Value(type))); } diff --git a/lib/services/audio_player/audio_player_impl.dart b/lib/services/audio_player/audio_player_impl.dart index 82c8c906..afd209a3 100644 --- a/lib/services/audio_player/audio_player_impl.dart +++ b/lib/services/audio_player/audio_player_impl.dart @@ -131,4 +131,8 @@ class SpotubeAudioPlayer extends AudioPlayerInterface Future setAudioNormalization(bool normalize) async { await _mkPlayer.setAudioNormalization(normalize); } + + Future setDemuxerBufferSize(int sizeInBytes) async { + await _mkPlayer.setDemuxerBufferSize(sizeInBytes); + } } diff --git a/lib/services/audio_player/custom_player.dart b/lib/services/audio_player/custom_player.dart index 5258696b..39866dcc 100644 --- a/lib/services/audio_player/custom_player.dart +++ b/lib/services/audio_player/custom_player.dart @@ -133,4 +133,12 @@ class CustomPlayer extends Player { await nativePlayer.setProperty('af', ''); } } + + Future setDemuxerBufferSize(int sizeInBytes) async { + await nativePlayer.setProperty('demuxer-max-bytes', sizeInBytes.toString()); + await nativePlayer.setProperty( + 'demuxer-max-back-bytes', + sizeInBytes.toString(), + ); + } } diff --git a/lib/services/sourced_track/enums.dart b/lib/services/sourced_track/enums.dart index 4c5ef8bc..9a1a5040 100644 --- a/lib/services/sourced_track/enums.dart +++ b/lib/services/sourced_track/enums.dart @@ -3,13 +3,15 @@ import 'package:spotube/models/playback/track_sources.dart'; enum SourceCodecs { m4a._("M4a (Best for downloaded music)"), weba._("WebA (Best for streamed music)\nDoesn't support audio metadata"), - mp3._("MP3 (Widely supported audio format)"); + mp3._("MP3 (Widely supported audio format)"), + flac._("FLAC (Lossless, best quality)\nLarge file size"); final String label; const SourceCodecs._(this.label); } enum SourceQualities { + uncompressed(3), high(2), medium(1), low(0); diff --git a/lib/services/sourced_track/sourced_track.dart b/lib/services/sourced_track/sourced_track.dart index 0d8d0a73..a6abdb20 100644 --- a/lib/services/sourced_track/sourced_track.dart +++ b/lib/services/sourced_track/sourced_track.dart @@ -212,7 +212,10 @@ abstract class SourcedTrack extends BasicSourcedTrack { final preferences = ref.read(userPreferencesProvider); return switch (preferences.audioSource) { - AudioSource.dabMusic => SourceCodecs.mp3, + AudioSource.dabMusic => + preferences.audioQuality == SourceQualities.uncompressed + ? SourceCodecs.flac + : SourceCodecs.mp3, AudioSource.jiosaavn => SourceCodecs.m4a, _ => preferences.streamMusicCodec }; diff --git a/lib/services/sourced_track/sources/dab_music.dart b/lib/services/sourced_track/sources/dab_music.dart index 20ec68f1..93293bd3 100644 --- a/lib/services/sourced_track/sources/dab_music.dart +++ b/lib/services/sourced_track/sources/dab_music.dart @@ -56,9 +56,10 @@ class DABMusicSourcedTrack extends SourcedTrack { SourceQualities quality, ) async { try { + final isUncompressed = quality == SourceQualities.uncompressed; final streamResponse = await dabMusicApiClient.music.getStream( trackId: id, - quality: "5", // mp3 320kbps (best available) + quality: isUncompressed ? "27" : "5", ); if (streamResponse.url == null) { throw Exception("No stream URL found for track ID: $id"); @@ -66,9 +67,11 @@ class DABMusicSourcedTrack extends SourcedTrack { return [ TrackSource( url: streamResponse.url!, - quality: SourceQualities.high, - bitrate: "320kbps", - codec: SourceCodecs.mp3, + quality: isUncompressed + ? SourceQualities.uncompressed + : SourceQualities.high, + bitrate: isUncompressed ? "2998kbps" : "320kbps", + codec: isUncompressed ? SourceCodecs.flac : SourceCodecs.mp3, ), ]; } catch (e, stackTrace) { @@ -126,7 +129,7 @@ class DABMusicSourcedTrack extends SourcedTrack { if (results.isEmpty) { final res = await dabMusicApiClient.music.getSearch( q: SourcedTrack.getSearchTerm(query), - limit: 20, + limit: 5, ); results = res.tracks ?? []; } diff --git a/untranslated_messages.json b/untranslated_messages.json index 08dfdf16..2334c1b3 100644 --- a/untranslated_messages.json +++ b/untranslated_messages.json @@ -1,118 +1,147 @@ { "ar": [ - "source" + "source", + "uncompressed" ], "bn": [ - "source" + "source", + "uncompressed" ], "ca": [ - "source" + "source", + "uncompressed" ], "cs": [ - "source" + "source", + "uncompressed" ], "de": [ - "source" + "source", + "uncompressed" ], "es": [ - "source" + "source", + "uncompressed" ], "eu": [ - "source" + "source", + "uncompressed" ], "fa": [ - "source" + "source", + "uncompressed" ], "fi": [ - "source" + "source", + "uncompressed" ], "fr": [ - "source" + "source", + "uncompressed" ], "hi": [ - "source" + "source", + "uncompressed" ], "id": [ - "source" + "source", + "uncompressed" ], "it": [ - "source" + "source", + "uncompressed" ], "ja": [ - "source" + "source", + "uncompressed" ], "ka": [ - "source" + "source", + "uncompressed" ], "ko": [ - "source" + "source", + "uncompressed" ], "ne": [ - "source" + "source", + "uncompressed" ], "nl": [ "audio_source", - "source" + "source", + "uncompressed" ], "pl": [ - "source" + "source", + "uncompressed" ], "pt": [ - "source" + "source", + "uncompressed" ], "ru": [ - "source" + "source", + "uncompressed" ], "ta": [ - "source" + "source", + "uncompressed" ], "th": [ - "source" + "source", + "uncompressed" ], "tl": [ - "source" + "source", + "uncompressed" ], "tr": [ - "source" + "source", + "uncompressed" ], "uk": [ - "source" + "source", + "uncompressed" ], "vi": [ - "source" + "source", + "uncompressed" ], "zh": [ - "source" + "source", + "uncompressed" ], "zh_TW": [ - "source" + "source", + "uncompressed" ] }