feat: Add download speed to progress bar

This commit is contained in:
PartyDonut 2025-08-29 19:29:47 +02:00
parent d22d340181
commit 5a5a4e4703
7 changed files with 95 additions and 68 deletions

View file

@ -1,23 +1,59 @@
import 'dart:async';
import 'package:flutter/widgets.dart';
import 'package:background_downloader/background_downloader.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:fladder/models/syncing/download_stream.dart';
import 'package:fladder/providers/settings/client_settings_provider.dart';
import 'package:fladder/providers/sync_provider.dart';
import 'package:fladder/util/localization_helper.dart';
part 'background_download_provider.g.dart';
final itemDownloadGroup = "ITEM_DOWNLOAD_GROUP";
@Riverpod(keepAlive: true)
class BackgroundDownloader extends _$BackgroundDownloader {
late StreamSubscription<TaskUpdate> updateListener;
@override
FileDownloader build() {
ref.onDispose(
() => updateListener.cancel(),
);
final maxDownloads = ref.read(clientSettingsProvider.select((value) => value.maxConcurrentDownloads));
return FileDownloader()
final downloader = FileDownloader()
..configure(
globalConfig: globalConfig(maxDownloads),
)
..trackTasks();
updateListener = downloader.updates.listen(updateTask);
return downloader;
}
void updateTask(TaskUpdate update) {
switch (update) {
case TaskStatusUpdate():
final status = update.status;
ref.read(downloadTasksProvider(update.task.taskId).notifier).update(
(state) => state.copyWith(status: status),
);
if (status == TaskStatus.complete || status == TaskStatus.canceled) {
ref.read(downloadTasksProvider(update.task.taskId).notifier).update((state) => DownloadStream.empty());
}
case TaskProgressUpdate():
final progress = update.progress;
ref.read(downloadTasksProvider(update.task.taskId).notifier).update(
(state) => state.copyWith(
progress: progress > 0 && progress < 1 ? progress : null,
downloadSpeed: update.networkSpeedAsString,
),
);
}
}
void setMaxConcurrent(int value) {

View file

@ -44,7 +44,7 @@ import 'package:fladder/util/localization_helper.dart';
final syncProvider = StateNotifierProvider<SyncNotifier, SyncSettingsModel>((ref) => throw UnimplementedError());
final downloadTasksProvider = StateProvider.family<DownloadStream, String>((ref, id) => DownloadStream.empty());
final downloadTasksProvider = StateProvider.family<DownloadStream, String?>((ref, id) => DownloadStream.empty());
class SyncNotifier extends StateNotifier<SyncSettingsModel> {
SyncNotifier(this.ref, this.mobileDirectory) : super(SyncSettingsModel()) {
@ -315,17 +315,13 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
)
.toList());
if (item.taskId != null) {
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(item.taskId!);
}
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(item.id);
await _db.deleteAllItems([...nestedChildren, item]);
for (var i = 0; i < nestedChildren.length; i++) {
final element = nestedChildren[i];
if (element.taskId != null) {
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(element.taskId!);
}
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(element.id);
if (await element.directory.exists()) {
await element.directory.delete(recursive: true);
}
@ -456,16 +452,14 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
ref.read(downloadTasksProvider(syncedItem.id).notifier).update((state) => DownloadStream.empty());
final taskId = task?.taskId;
if (taskId != null) {
ref.read(backgroundDownloaderProvider).cancelTaskWithId(taskId);
}
ref.read(backgroundDownloaderProvider).cancelTaskWithId(syncedItem.id);
cleanupTemporaryFiles();
refresh();
return syncedItem;
}
Future<DownloadStream?> syncFile(SyncedItem syncItem, bool skipDownload) async {
Future<bool?> syncFile(SyncedItem syncItem, bool skipDownload) async {
cleanupTemporaryFiles();
final playbackResponse = await api.itemsItemIdPlaybackInfoPost(
@ -505,8 +499,12 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
final downloadUrl = path.joinAll([user.server, "Items", syncItem.id, "Download"]);
try {
if (!skipDownload && currentTask.task == null) {
if (currentTask.task != null) {
await ref.read(backgroundDownloaderProvider).cancelTaskWithId(currentTask.id);
}
if (!skipDownload) {
final downloadTask = DownloadTask(
taskId: syncItem.id,
url: Uri.parse(downloadUrl).toString(),
directory: syncItem.directory.path,
filename: syncItem.videoFileName,
@ -519,36 +517,9 @@ class SyncNotifier extends StateNotifier<SyncSettingsModel> {
allowPause: true,
);
final defaultDownloadStream =
DownloadStream(id: syncItem.id, task: downloadTask, progress: 0.0, status: TaskStatus.enqueued);
final defaultDownloadStream = DownloadStream(id: syncItem.id, task: downloadTask, status: TaskStatus.enqueued);
ref.read(downloadTasksProvider(syncItem.id).notifier).update((state) => defaultDownloadStream);
ref.read(backgroundDownloaderProvider).download(
downloadTask,
onProgress: (progress) {
if (progress > 0 && progress < 1) {
ref.read(downloadTasksProvider(syncItem.id).notifier).update(
(state) => state.copyWith(progress: progress),
);
} else {
ref.read(downloadTasksProvider(syncItem.id).notifier).update(
(state) => state.copyWith(progress: null),
);
}
},
onStatus: (status) {
ref.read(downloadTasksProvider(syncItem.id).notifier).update(
(state) => state.copyWith(status: status),
);
if (status == TaskStatus.complete || status == TaskStatus.canceled) {
ref.read(downloadTasksProvider(syncItem.id).notifier).update((state) => DownloadStream.empty());
}
},
);
return defaultDownloadStream;
return await ref.read(backgroundDownloaderProvider).enqueue(downloadTask);
}
} catch (e) {
log(e.toString());