From 8acd880329220c6ef6bc027511343dd0e34a44c7 Mon Sep 17 00:00:00 2001 From: PartyDonut <42371342+PartyDonut@users.noreply.github.com> Date: Sun, 18 May 2025 10:14:23 +0200 Subject: [PATCH] feat: Add max concurrent downloads to settings (#347) Co-authored-by: PartyDonut --- lib/l10n/app_en.arb | 4 +- .../settings/client_settings_model.dart | 1 + .../client_settings_model.freezed.dart | 27 ++++++++- .../settings/client_settings_model.g.dart | 3 + .../sync/background_download_provider.dart | 56 ++++++++++++++++--- .../sync/background_download_provider.g.dart | 15 +++-- .../client_settings_download.dart | 24 +++++++- 7 files changed, 110 insertions(+), 20 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f392f79..9fe40f7 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1199,5 +1199,7 @@ "example": "1" } } - } + }, + "maxConcurrentDownloadsTitle": "Max concurrent downloads", + "maxConcurrentDownloadsDesc": "Sets the maximum number of downloads that can run at the same time. Set to 0 to disable the limit." } \ No newline at end of file diff --git a/lib/models/settings/client_settings_model.dart b/lib/models/settings/client_settings_model.dart index f6a8366..688f3ed 100644 --- a/lib/models/settings/client_settings_model.dart +++ b/lib/models/settings/client_settings_model.dart @@ -32,6 +32,7 @@ class ClientSettingsModel with _$ClientSettingsModel { @Default(false) bool mouseDragSupport, @Default(true) bool requireWifi, @Default(false) bool showAllCollectionTypes, + @Default(2) int maxConcurrentDownloads, @Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant, int? libraryPageSize, }) = _ClientSettingsModel; diff --git a/lib/models/settings/client_settings_model.freezed.dart b/lib/models/settings/client_settings_model.freezed.dart index 3bb7f64..50b2f0a 100644 --- a/lib/models/settings/client_settings_model.freezed.dart +++ b/lib/models/settings/client_settings_model.freezed.dart @@ -38,6 +38,7 @@ mixin _$ClientSettingsModel { bool get mouseDragSupport => throw _privateConstructorUsedError; bool get requireWifi => throw _privateConstructorUsedError; bool get showAllCollectionTypes => throw _privateConstructorUsedError; + int get maxConcurrentDownloads => throw _privateConstructorUsedError; DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError; int? get libraryPageSize => throw _privateConstructorUsedError; @@ -75,6 +76,7 @@ abstract class $ClientSettingsModelCopyWith<$Res> { bool mouseDragSupport, bool requireWifi, bool showAllCollectionTypes, + int maxConcurrentDownloads, DynamicSchemeVariant schemeVariant, int? libraryPageSize}); } @@ -111,6 +113,7 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel> Object? mouseDragSupport = null, Object? requireWifi = null, Object? showAllCollectionTypes = null, + Object? maxConcurrentDownloads = null, Object? schemeVariant = null, Object? libraryPageSize = freezed, }) { @@ -183,6 +186,10 @@ class _$ClientSettingsModelCopyWithImpl<$Res, $Val extends ClientSettingsModel> ? _value.showAllCollectionTypes : showAllCollectionTypes // ignore: cast_nullable_to_non_nullable as bool, + maxConcurrentDownloads: null == maxConcurrentDownloads + ? _value.maxConcurrentDownloads + : maxConcurrentDownloads // ignore: cast_nullable_to_non_nullable + as int, schemeVariant: null == schemeVariant ? _value.schemeVariant : schemeVariant // ignore: cast_nullable_to_non_nullable @@ -221,6 +228,7 @@ abstract class _$$ClientSettingsModelImplCopyWith<$Res> bool mouseDragSupport, bool requireWifi, bool showAllCollectionTypes, + int maxConcurrentDownloads, DynamicSchemeVariant schemeVariant, int? libraryPageSize}); } @@ -255,6 +263,7 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res> Object? mouseDragSupport = null, Object? requireWifi = null, Object? showAllCollectionTypes = null, + Object? maxConcurrentDownloads = null, Object? schemeVariant = null, Object? libraryPageSize = freezed, }) { @@ -327,6 +336,10 @@ class __$$ClientSettingsModelImplCopyWithImpl<$Res> ? _value.showAllCollectionTypes : showAllCollectionTypes // ignore: cast_nullable_to_non_nullable as bool, + maxConcurrentDownloads: null == maxConcurrentDownloads + ? _value.maxConcurrentDownloads + : maxConcurrentDownloads // ignore: cast_nullable_to_non_nullable + as int, schemeVariant: null == schemeVariant ? _value.schemeVariant : schemeVariant // ignore: cast_nullable_to_non_nullable @@ -361,6 +374,7 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel this.mouseDragSupport = false, this.requireWifi = true, this.showAllCollectionTypes = false, + this.maxConcurrentDownloads = 2, this.schemeVariant = DynamicSchemeVariant.tonalSpot, this.libraryPageSize}) : super._(); @@ -418,13 +432,16 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel final bool showAllCollectionTypes; @override @JsonKey() + final int maxConcurrentDownloads; + @override + @JsonKey() final DynamicSchemeVariant schemeVariant; @override final int? libraryPageSize; @override String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { - return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, schemeVariant: $schemeVariant, libraryPageSize: $libraryPageSize)'; + return 'ClientSettingsModel(syncPath: $syncPath, position: $position, size: $size, timeOut: $timeOut, nextUpDateCutoff: $nextUpDateCutoff, themeMode: $themeMode, themeColor: $themeColor, amoledBlack: $amoledBlack, blurPlaceHolders: $blurPlaceHolders, blurUpcomingEpisodes: $blurUpcomingEpisodes, selectedLocale: $selectedLocale, enableMediaKeys: $enableMediaKeys, posterSize: $posterSize, pinchPosterZoom: $pinchPosterZoom, mouseDragSupport: $mouseDragSupport, requireWifi: $requireWifi, showAllCollectionTypes: $showAllCollectionTypes, maxConcurrentDownloads: $maxConcurrentDownloads, schemeVariant: $schemeVariant, libraryPageSize: $libraryPageSize)'; } @override @@ -450,6 +467,8 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel ..add(DiagnosticsProperty('requireWifi', requireWifi)) ..add( DiagnosticsProperty('showAllCollectionTypes', showAllCollectionTypes)) + ..add( + DiagnosticsProperty('maxConcurrentDownloads', maxConcurrentDownloads)) ..add(DiagnosticsProperty('schemeVariant', schemeVariant)) ..add(DiagnosticsProperty('libraryPageSize', libraryPageSize)); } @@ -491,6 +510,8 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel other.requireWifi == requireWifi) && (identical(other.showAllCollectionTypes, showAllCollectionTypes) || other.showAllCollectionTypes == showAllCollectionTypes) && + (identical(other.maxConcurrentDownloads, maxConcurrentDownloads) || + other.maxConcurrentDownloads == maxConcurrentDownloads) && (identical(other.schemeVariant, schemeVariant) || other.schemeVariant == schemeVariant) && (identical(other.libraryPageSize, libraryPageSize) || @@ -518,6 +539,7 @@ class _$ClientSettingsModelImpl extends _ClientSettingsModel mouseDragSupport, requireWifi, showAllCollectionTypes, + maxConcurrentDownloads, schemeVariant, libraryPageSize ]); @@ -558,6 +580,7 @@ abstract class _ClientSettingsModel extends ClientSettingsModel { final bool mouseDragSupport, final bool requireWifi, final bool showAllCollectionTypes, + final int maxConcurrentDownloads, final DynamicSchemeVariant schemeVariant, final int? libraryPageSize}) = _$ClientSettingsModelImpl; _ClientSettingsModel._() : super._(); @@ -601,6 +624,8 @@ abstract class _ClientSettingsModel extends ClientSettingsModel { @override bool get showAllCollectionTypes; @override + int get maxConcurrentDownloads; + @override DynamicSchemeVariant get schemeVariant; @override int? get libraryPageSize; diff --git a/lib/models/settings/client_settings_model.g.dart b/lib/models/settings/client_settings_model.g.dart index d870f72..c2a34fa 100644 --- a/lib/models/settings/client_settings_model.g.dart +++ b/lib/models/settings/client_settings_model.g.dart @@ -36,6 +36,8 @@ _$ClientSettingsModelImpl _$$ClientSettingsModelImplFromJson( mouseDragSupport: json['mouseDragSupport'] as bool? ?? false, requireWifi: json['requireWifi'] as bool? ?? true, showAllCollectionTypes: json['showAllCollectionTypes'] as bool? ?? false, + maxConcurrentDownloads: + (json['maxConcurrentDownloads'] as num?)?.toInt() ?? 2, schemeVariant: $enumDecodeNullable( _$DynamicSchemeVariantEnumMap, json['schemeVariant']) ?? DynamicSchemeVariant.tonalSpot, @@ -62,6 +64,7 @@ Map _$$ClientSettingsModelImplToJson( 'mouseDragSupport': instance.mouseDragSupport, 'requireWifi': instance.requireWifi, 'showAllCollectionTypes': instance.showAllCollectionTypes, + 'maxConcurrentDownloads': instance.maxConcurrentDownloads, 'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!, 'libraryPageSize': instance.libraryPageSize, }; diff --git a/lib/providers/sync/background_download_provider.dart b/lib/providers/sync/background_download_provider.dart index fc5fc79..ae154a2 100644 --- a/lib/providers/sync/background_download_provider.dart +++ b/lib/providers/sync/background_download_provider.dart @@ -1,17 +1,55 @@ import 'package:background_downloader/background_downloader.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:fladder/providers/settings/client_settings_provider.dart'; + part 'background_download_provider.g.dart'; @Riverpod(keepAlive: true) -FileDownloader backgroundDownloader(Ref ref) { - return FileDownloader() - ..trackTasks() - ..configureNotification( - running: const TaskNotification('Downloading', 'file: {filename}'), - complete: const TaskNotification('Download finished', 'file: {filename}'), - paused: const TaskNotification('Download paused', 'file: {filename}'), - progressBar: true, +class BackgroundDownloader extends _$BackgroundDownloader { + @override + FileDownloader build() { + final maxDownloads = ref.read(clientSettingsProvider.select((value) => value.maxConcurrentDownloads)); + return FileDownloader() + ..configure( + globalConfig: maxDownloads == 0 + ? ("", "") + : ( + Config.holdingQueue, + ( + //maxConcurrent + maxDownloads, + //maxConcurrentByHost + maxDownloads, + //maxConcurrentByGroup + maxDownloads, + ), + ), + ) + ..trackTasks() + ..configureNotification( + running: const TaskNotification('Downloading', 'file: {filename}'), + complete: const TaskNotification('Download finished', 'file: {filename}'), + paused: const TaskNotification('Download paused', 'file: {filename}'), + progressBar: true, + ); + } + + void setMaxConcurrent(int value) { + state.configure( + globalConfig: value == 0 + ? ("", "") + : ( + Config.holdingQueue, + ( + //maxConcurrent + value, + //maxConcurrentByHost + value, + //maxConcurrentByGroup + value, + ), + ), ); + } } diff --git a/lib/providers/sync/background_download_provider.g.dart b/lib/providers/sync/background_download_provider.g.dart index 47ffb02..45e6dd2 100644 --- a/lib/providers/sync/background_download_provider.g.dart +++ b/lib/providers/sync/background_download_provider.g.dart @@ -7,12 +7,13 @@ part of 'background_download_provider.dart'; // ************************************************************************** String _$backgroundDownloaderHash() => - r'997d9f4ba79dd0d9d30d5f283b36d5280d10dfaa'; + r'df72b6338a8e80178935985ba17c43bf720f4522'; -/// See also [backgroundDownloader]. -@ProviderFor(backgroundDownloader) -final backgroundDownloaderProvider = Provider.internal( - backgroundDownloader, +/// See also [BackgroundDownloader]. +@ProviderFor(BackgroundDownloader) +final backgroundDownloaderProvider = + NotifierProvider.internal( + BackgroundDownloader.new, name: r'backgroundDownloaderProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') ? null @@ -21,8 +22,6 @@ final backgroundDownloaderProvider = Provider.internal( allTransitiveDependencies: null, ); -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef BackgroundDownloaderRef = ProviderRef; +typedef _$BackgroundDownloader = Notifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/screens/settings/client_sections/client_settings_download.dart b/lib/screens/settings/client_sections/client_settings_download.dart index c54ee32..75303bd 100644 --- a/lib/screens/settings/client_sections/client_settings_download.dart +++ b/lib/screens/settings/client_sections/client_settings_download.dart @@ -1,16 +1,18 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:iconsax_plus/iconsax_plus.dart'; import 'package:fladder/providers/settings/client_settings_provider.dart'; +import 'package:fladder/providers/sync/background_download_provider.dart'; import 'package:fladder/providers/sync_provider.dart'; import 'package:fladder/providers/user_provider.dart'; import 'package:fladder/screens/settings/settings_list_tile.dart'; import 'package:fladder/screens/settings/widgets/settings_label_divider.dart'; import 'package:fladder/screens/shared/default_alert_dialog.dart'; +import 'package:fladder/screens/shared/input_fields.dart'; import 'package:fladder/util/adaptive_layout.dart'; import 'package:fladder/util/localization_helper.dart'; import 'package:fladder/util/size_formatting.dart'; @@ -116,6 +118,26 @@ List buildClientSettingsDownload(BuildContext context, WidgetRef ref, Fu onChanged: (value) => ref.read(clientSettingsProvider.notifier).setRequireWifi(value), ), ), + SettingsListTile( + label: Text(context.localized.maxConcurrentDownloadsTitle), + subLabel: Text(context.localized.maxConcurrentDownloadsDesc), + trailing: SizedBox( + width: 100, + child: IntInputField( + controller: TextEditingController(text: clientSettings.maxConcurrentDownloads.toString()), + onSubmitted: (value) { + if (value != null) { + ref.read(clientSettingsProvider.notifier).update( + (current) => current.copyWith( + maxConcurrentDownloads: value, + ), + ); + + ref.read(backgroundDownloaderProvider.notifier).setMaxConcurrent(value); + } + }, + )), + ), const Divider(), ], ];