diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 5756f49c..08385e00 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -819,8 +819,16 @@ }, "bufferDuration": "Buffer Duration", "@bufferDuration": {}, - "bufferDurationSubtitle": "How much the player should buffer, in seconds. Requires a restart.", + "bufferDurationSubtitle": "The maximum duration that should be buffered, in seconds. Requires a restart.", "@bufferDurationSubtitle": {}, + "bufferDisableSizeConstraintsTitle": "Don't limit buffer size", + "@bufferDisableSizeConstraintsTitle": {}, + "bufferDisableSizeConstraintsSubtitle": "Disables the buffer size constraints ('Buffer Size'). The buffer will always be loaded to the configured duration ('Buffer Duration'), even for very large files. Can cause crashes. Requires a restart.", + "@bufferDisableSizeConstraintsSubtitle": {}, + "bufferSizeTitle": "Buffer Size", + "@bufferSizeTitle": {}, + "bufferSizeSubtitle": "The maximum size of the buffer in MB. Requires a restart", + "@bufferSizeSubtitle": {}, "language": "Language", "@language": {}, "skipToPreviousTrackButtonTooltip": "Skip to beginning or to previous track", diff --git a/lib/models/finamp_models.dart b/lib/models/finamp_models.dart index 86bc80cd..bcfc4061 100644 --- a/lib/models/finamp_models.dart +++ b/lib/models/finamp_models.dart @@ -85,7 +85,9 @@ class DefaultSettings { static const showArtistsTopSongs = true; static const disableGesture = false; static const showFastScroller = true; + static const bufferDisableSizeConstraints = false; static const bufferDurationSeconds = 600; + static const bufferSizeMegabytes = 50; static const tabOrder = TabContentType.values; static const swipeInsertQueueNext = true; static const loopMode = FinampLoopMode.none; @@ -175,7 +177,10 @@ class FinampSettings { this.playerScreenCoverMinimumPadding = DefaultSettings.playerScreenCoverMinimumPadding, this.showArtistsTopSongs = DefaultSettings.showArtistsTopSongs, + this.bufferDisableSizeConstraints = + DefaultSettings.bufferDisableSizeConstraints, this.bufferDurationSeconds = DefaultSettings.bufferDurationSeconds, + this.bufferSizeMegabytes = DefaultSettings.bufferSizeMegabytes, required this.tabSortBy, required this.tabSortOrder, this.loopMode = DefaultSettings.loopMode, @@ -498,6 +503,12 @@ class FinampSettings { @HiveField(77, defaultValue: DefaultSettings.showCoversOnAlbumScreen) bool showCoversOnAlbumScreen; + @HiveField(78, defaultValue: DefaultSettings.bufferDisableSizeConstraints) + bool bufferDisableSizeConstraints; + + @HiveField(79, defaultValue: DefaultSettings.bufferSizeMegabytes) + int bufferSizeMegabytes; + static Future create() async { final downloadLocation = await DownloadLocation.create( name: "Internal Storage", diff --git a/lib/models/finamp_models.g.dart b/lib/models/finamp_models.g.dart index 6dad8be1..51b8a811 100644 --- a/lib/models/finamp_models.g.dart +++ b/lib/models/finamp_models.g.dart @@ -102,7 +102,10 @@ class FinampSettingsAdapter extends TypeAdapter { playerScreenCoverMinimumPadding: fields[48] == null ? 1.5 : fields[48] as double, showArtistsTopSongs: fields[54] == null ? true : fields[54] as bool, + bufferDisableSizeConstraints: + fields[78] == null ? false : fields[78] as bool, bufferDurationSeconds: fields[18] == null ? 600 : fields[18] as int, + bufferSizeMegabytes: fields[79] == null ? 50 : fields[79] as int, tabSortBy: fields[20] == null ? {} : (fields[20] as Map).cast(), @@ -200,7 +203,7 @@ class FinampSettingsAdapter extends TypeAdapter { @override void write(BinaryWriter writer, FinampSettings obj) { writer - ..writeByte(74) + ..writeByte(76) ..writeByte(0) ..write(obj.isOffline) ..writeByte(1) @@ -348,7 +351,11 @@ class FinampSettingsAdapter extends TypeAdapter { ..writeByte(76) ..write(obj.featureChipsConfiguration) ..writeByte(77) - ..write(obj.showCoversOnAlbumScreen); + ..write(obj.showCoversOnAlbumScreen) + ..writeByte(78) + ..write(obj.bufferDisableSizeConstraints) + ..writeByte(79) + ..write(obj.bufferSizeMegabytes); } @override diff --git a/lib/screens/audio_service_settings_screen.dart b/lib/screens/audio_service_settings_screen.dart index d07c13ed..106efba4 100644 --- a/lib/screens/audio_service_settings_screen.dart +++ b/lib/screens/audio_service_settings_screen.dart @@ -1,8 +1,10 @@ import 'dart:io'; +import 'package:finamp/models/finamp_models.dart'; import 'package:finamp/services/finamp_settings_helper.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:hive/hive.dart'; import '../components/AudioServiceSettingsScreen/buffer_duration_list_tile.dart'; import '../components/AudioServiceSettingsScreen/loadQueueOnStartup_selector.dart'; @@ -43,7 +45,9 @@ class _AudioServiceSettingsScreenState children: [ if (Platform.isAndroid) StopForegroundSelector(key: _updateChildren), SongShuffleItemCountEditor(key: _updateChildren), + if (Platform.isAndroid) BufferSizeListTile(key: _updateChildren), BufferDurationListTile(key: _updateChildren), + BufferDisableSizeConstraintsSelector(key: _updateChildren), const LoadQueueOnStartupSelector(), PeriodicPlaybackSessionUpdateFrequencyEditor(key: _updateChildren), const ReportQueueToServerToggle(), @@ -52,3 +56,76 @@ class _AudioServiceSettingsScreenState ); } } + + +class BufferDisableSizeConstraintsSelector extends StatelessWidget { + const BufferDisableSizeConstraintsSelector({super.key}); + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder>( + valueListenable: FinampSettingsHelper.finampSettingsListener, + builder: (_, box, __) { + return SwitchListTile.adaptive( + title: Text( + AppLocalizations.of(context)!.bufferDisableSizeConstraintsTitle), + subtitle: Text(AppLocalizations.of(context)! + .bufferDisableSizeConstraintsSubtitle), + value: + FinampSettingsHelper.finampSettings.bufferDisableSizeConstraints, + onChanged: (value) { + FinampSettings finampSettingsTemp = + FinampSettingsHelper.finampSettings; + finampSettingsTemp.bufferDisableSizeConstraints = value; + Hive.box("FinampSettings") + .put("FinampSettings", finampSettingsTemp); + }, + ); + }, + ); + } +} + +class BufferSizeListTile extends StatefulWidget { + const BufferSizeListTile({super.key}); + + @override + State createState() => _BufferSizeListTileState(); +} + +class _BufferSizeListTileState extends State { + final _controller = TextEditingController( + text: FinampSettingsHelper.finampSettings.bufferSizeMegabytes.toString()); + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text(AppLocalizations.of(context)!.bufferSizeTitle), + subtitle: Text(AppLocalizations.of(context)!.bufferSizeSubtitle), + trailing: SizedBox( + width: 50 * MediaQuery.of(context).textScaleFactor, + child: TextField( + controller: _controller, + textAlign: TextAlign.center, + keyboardType: TextInputType.number, + onChanged: (value) { + var valueInt = int.tryParse(value); + + if (valueInt != null && !valueInt.isNegative) { + // minimum buffer size is 60, if we go below that, the player will crash + if (valueInt < 60) { + _controller.text = "60"; + valueInt = 60; + } + FinampSettings finampSettingsTemp = + FinampSettingsHelper.finampSettings; + finampSettingsTemp.bufferSizeMegabytes = valueInt; + Hive.box("FinampSettings") + .put("FinampSettings", finampSettingsTemp); + } + }, + ), + ), + ); + } +} diff --git a/lib/services/music_player_background_task.dart b/lib/services/music_player_background_task.dart index df52eca0..eda79f7a 100644 --- a/lib/services/music_player_background_task.dart +++ b/lib/services/music_player_background_task.dart @@ -143,15 +143,26 @@ class MusicPlayerBackgroundTask extends BaseAudioHandler { _player = AudioPlayer( audioLoadConfiguration: AudioLoadConfiguration( androidLoadControl: AndroidLoadControl( - minBufferDuration: FinampSettingsHelper.finampSettings.bufferDuration, - maxBufferDuration: FinampSettingsHelper - .finampSettings.bufferDuration * - 1.5, // allows the player to fetch a bit more data in exchange for reduced request frequency - prioritizeTimeOverSizeThresholds: true, + targetBufferBytes: + FinampSettingsHelper.finampSettings.bufferDisableSizeConstraints + ? null + : 1024 * + 1024 * + FinampSettingsHelper.finampSettings.bufferSizeMegabytes, + // minBufferDuration: FinampSettingsHelper.finampSettings.bufferDuration, + minBufferDuration: Duration(seconds: 60), // when to fetch more data + maxBufferDuration: FinampSettingsHelper.finampSettings + .bufferDuration, // allows the player to fetch a bit more data in exchange for reduced request frequency + prioritizeTimeOverSizeThresholds: FinampSettingsHelper.finampSettings + .bufferDisableSizeConstraints, // targetBufferBytes sets the absolute maximum, but if this false and maxBufferDuration is reached, buffering will end ), darwinLoadControl: DarwinLoadControl( + // preferredForwardBufferDuration: + // FinampSettingsHelper.finampSettings.bufferDuration, preferredForwardBufferDuration: - FinampSettingsHelper.finampSettings.bufferDuration, + FinampSettingsHelper.finampSettings.bufferDisableSizeConstraints + ? FinampSettingsHelper.finampSettings.bufferDuration + : null, // let system decide ), ), audioPipeline: _audioPipeline, diff --git a/pubspec.lock b/pubspec.lock index a4634472..e7838869 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,23 +5,23 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "76.0.0" + version: "72.0.0" _macros: dependency: transitive description: dart source: sdk - version: "0.3.3" + version: "0.3.2" analyzer: dependency: "direct overridden" description: name: analyzer - sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.11.0" + version: "6.7.0" analyzer_plugin: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: "direct main" description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.18.0" color: dependency: transitive description: @@ -847,18 +847,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -895,10 +895,10 @@ packages: dependency: transitive description: name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" url: "https://pub.dev" source: hosted - version: "0.1.3-main.0" + version: "0.1.2-main.4" marquee: dependency: "direct main" description: @@ -1353,7 +1353,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sliver_tools: dependency: transitive description: @@ -1431,10 +1431,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.1" state_notifier: dependency: transitive description: @@ -1463,10 +1463,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" synchronized: dependency: transitive description: @@ -1487,10 +1487,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.2" time: dependency: transitive description: @@ -1671,10 +1671,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.2.5" wakelock_plus: dependency: "direct main" description: