feat: add total size calculation for library items and improve download manager functionality

This commit is contained in:
Dr-Blank 2024-09-22 21:51:52 -04:00
parent 30f87c422b
commit b0cc1ff8cb
No known key found for this signature in database
GPG key ID: 7452CC63F210A266
5 changed files with 190 additions and 196 deletions

View file

@ -92,50 +92,13 @@ class AudiobookDownloadManager {
allowPause: allowPause, allowPause: allowPause,
group: item.id, group: item.id,
baseDirectory: downloadDirectory, baseDirectory: downloadDirectory,
updates: Updates.statusAndProgress,
// metaData: token // metaData: token
); );
// _downloadTasks.add(task); // _downloadTasks.add(task);
tq.add(task); tq.add(task);
_logger.info('queued task: ${task.taskId}'); _logger.info('queued task: ${task.taskId}');
} }
FileDownloader().registerCallbacks(
group: item.id,
taskProgressCallback: (update) {
try {
taskProgressCallback?.call(update);
} catch (e) {
_logger.warning('Error in taskProgressCallback: $e');
}
_logger.info('Group: ${item.id}, Progress Update: ${update.progress}');
},
taskStatusCallback: (update) {
try {
taskStatusCallback?.call(update);
} catch (e) {
_logger.warning('Error in taskStatusCallback: $e');
}
switch (update.status) {
case TaskStatus.complete:
_logger.info('Group: ${item.id}, Download Complete');
break;
case TaskStatus.failed:
_logger.warning('Group: ${item.id}, Download Failed');
break;
default:
_logger
.info('Group: ${item.id}, Download Status: ${update.status}');
}
},
taskNotificationTapCallback: (task, notificationType) {
try {
taskNotificationTapCallback?.call(task, notificationType);
} catch (e) {
_logger.warning('Error in taskNotificationTapCallback: $e');
}
_logger.info('Group: ${item.id}, Task: ${task.taskId}');
},
);
} }
String constructFilePath( String constructFilePath(
@ -152,10 +115,6 @@ class AudiobookDownloadManager {
} }
bool isItemDownloading(String id) { bool isItemDownloading(String id) {
if (tq.isEmpty) {
return false;
}
return tq.enqueued.any((task) => task.group == id); return tq.enqueued.any((task) => task.group == id);
} }
@ -164,6 +123,29 @@ class AudiobookDownloadManager {
return fileExists; return fileExists;
} }
Future<List<LibraryFile>> getDownloadedFilesMetadata(
LibraryItemExpanded item,
) async {
final directory = await getApplicationSupportDirectory();
final downloadedFiles = <LibraryFile>[];
for (final file in item.libraryFiles) {
final filePath = constructFilePath(directory, item, file);
if (isFileDownloaded(filePath)) {
downloadedFiles.add(file);
}
}
return downloadedFiles;
}
Future<int> getDownloadedSize(LibraryItemExpanded item) async {
final files = await getDownloadedFilesMetadata(item);
return files.fold<int>(
0,
(previousValue, element) => previousValue + element.metadata.size,
);
}
Future<bool> isItemDownloaded(LibraryItemExpanded item) async { Future<bool> isItemDownloaded(LibraryItemExpanded item) async {
final directory = await getApplicationSupportDirectory(); final directory = await getApplicationSupportDirectory();
for (final file in item.libraryFiles) { for (final file in item.libraryFiles) {
@ -188,7 +170,7 @@ class AudiobookDownloadManager {
} }
} }
Future<List<Uri>> getDownloadedFiles(LibraryItemExpanded item) async { Future<List<Uri>> getDownloadedFilesUri(LibraryItemExpanded item) async {
final directory = await getApplicationSupportDirectory(); final directory = await getApplicationSupportDirectory();
final files = <Uri>[]; final files = <Uri>[];
for (final file in item.libraryFiles) { for (final file in item.libraryFiles) {
@ -214,12 +196,12 @@ class AudiobookDownloadManager {
await fileDownloader.trackTasks(); await fileDownloader.trackTasks();
_logger.info('Listening to download manager updates');
try { try {
_updatesSubscription = fileDownloader.updates.listen((event) { _updatesSubscription = fileDownloader.updates.listen((event) {
_logger.info('Got event: $event'); _logger.finer('Got event: $event');
_taskStatusController.add(event); _taskStatusController.add(event);
}); });
_logger.info('Listening to download manager updates');
} catch (e) { } catch (e) {
_logger.warning('Error when listening to download manager updates: $e'); _logger.warning('Error when listening to download manager updates: $e');
} }

View file

@ -3,8 +3,10 @@ import 'package:logging/logging.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:shelfsdk/audiobookshelf_api.dart'; import 'package:shelfsdk/audiobookshelf_api.dart';
import 'package:vaani/api/api_provider.dart'; import 'package:vaani/api/api_provider.dart';
import 'package:vaani/api/library_item_provider.dart';
import 'package:vaani/features/downloads/core/download_manager.dart' as core; import 'package:vaani/features/downloads/core/download_manager.dart' as core;
import 'package:vaani/settings/app_settings_provider.dart'; import 'package:vaani/settings/app_settings_provider.dart';
import 'package:vaani/shared/extensions/item_files.dart';
part 'download_manager.g.dart'; part 'download_manager.g.dart';
@ -36,15 +38,11 @@ class SimpleDownloadManager extends _$SimpleDownloadManager {
} }
} }
@riverpod @Riverpod(keepAlive: true)
class DownloadManager extends _$DownloadManager { class DownloadManager extends _$DownloadManager {
@override @override
core.AudiobookDownloadManager build() { core.AudiobookDownloadManager build() {
final manager = ref.watch(simpleDownloadManagerProvider); final manager = ref.watch(simpleDownloadManagerProvider);
ref.onDispose(() {
manager.dispose();
});
manager.taskUpdateStream.listen((_) { manager.taskUpdateStream.listen((_) {
ref.notifyListeners(); ref.notifyListeners();
}); });
@ -87,49 +85,52 @@ class DownloadManager extends _$DownloadManager {
class IsItemDownloading extends _$IsItemDownloading { class IsItemDownloading extends _$IsItemDownloading {
@override @override
bool build(String id) { bool build(String id) {
final manager = ref.read(downloadManagerProvider); final manager = ref.watch(downloadManagerProvider);
ref.onDispose(() {
manager.dispose();
});
callback(dynamic _) {
ref.notifyListeners();
}
FileDownloader().registerCallbacks(
group: id,
taskStatusCallback: callback,
);
ref.onDispose(() {
FileDownloader().unregisterCallbacks(group: id, callback: callback);
});
return manager.isItemDownloading(id); return manager.isItemDownloading(id);
} }
} }
@Riverpod(keepAlive: true) @riverpod
class DownloadProgress extends _$DownloadProgress { class ItemDownloadProgress extends _$ItemDownloadProgress {
@override @override
double build(String id) { Future<double?> build(String id) async {
var progress = 0.0; final item = await ref.watch(libraryItemProvider(id).future);
final manager = ref.read(downloadManagerProvider);
void progressCallback(TaskProgressUpdate progress) { manager.taskUpdateStream.map((taskUpdate) {
state = progress.progress; if (taskUpdate is! TaskProgressUpdate) {
return null;
} }
if (taskUpdate.task.group == id) {
FileDownloader().registerCallbacks( return taskUpdate;
group: id, }
taskProgressCallback: progressCallback, }).listen((task) async {
if (task != null) {
final totalSize = item.totalSize;
// if total size is 0, return 0
if (totalSize == 0) {
state = const AsyncValue.data(0.0);
return;
}
final downloadedFiles = await manager.getDownloadedFilesMetadata(item);
// calculate total size of downloaded files and total size of item, then divide
// to get percentage
final downloadedSize = downloadedFiles.fold<int>(
0,
(previousValue, element) => previousValue + element.metadata.size,
); );
ref.onDispose(() { final inProgressFileSize = task.progress * task.expectedFileSize;
FileDownloader() final totalDownloadedSize = downloadedSize + inProgressFileSize;
.unregisterCallbacks(group: id, callback: progressCallback); final progress = totalDownloadedSize / totalSize;
}); // if current progress is more than calculated progress, do not update
if (progress < (state.valueOrNull ?? 0.0)) {
return;
}
return progress; state = AsyncValue.data(progress.clamp(0.0, 1.0));
}
});
return null;
} }
} }
@ -141,17 +142,8 @@ FutureOr<List<TaskRecord>> downloadHistory(
return await FileDownloader().database.allRecords(group: group); return await FileDownloader().database.allRecords(group: group);
} }
// @Riverpod(keepAlive: false)
// FutureOr<bool> downloadStatus(
// DownloadStatusRef ref,
// LibraryItemExpanded item,
// ) async {
// final manager = ref.read(simpleDownloadManagerProvider);
// return manager.isItemDownloaded(item);
// }
@riverpod @riverpod
class DownloadStatus extends _$DownloadStatus { class IsItemDownloaded extends _$IsItemDownloaded {
@override @override
FutureOr<bool> build( FutureOr<bool> build(
LibraryItemExpanded item, LibraryItemExpanded item,
@ -159,5 +151,4 @@ class DownloadStatus extends _$DownloadStatus {
final manager = ref.watch(downloadManagerProvider); final manager = ref.watch(downloadManagerProvider);
return manager.isItemDownloaded(item); return manager.isItemDownloaded(item);
} }
} }

View file

@ -174,12 +174,12 @@ final simpleDownloadManagerProvider = NotifierProvider<SimpleDownloadManager,
); );
typedef _$SimpleDownloadManager = Notifier<core.AudiobookDownloadManager>; typedef _$SimpleDownloadManager = Notifier<core.AudiobookDownloadManager>;
String _$downloadManagerHash() => r'bf08bdeda54773a4b6713ad77abb75add3ed30ee'; String _$downloadManagerHash() => r'9566b772d792b32e1b199d4aa834e28de3b034d0';
/// See also [DownloadManager]. /// See also [DownloadManager].
@ProviderFor(DownloadManager) @ProviderFor(DownloadManager)
final downloadManagerProvider = AutoDisposeNotifierProvider<DownloadManager, final downloadManagerProvider =
core.AudiobookDownloadManager>.internal( NotifierProvider<DownloadManager, core.AudiobookDownloadManager>.internal(
DownloadManager.new, DownloadManager.new,
name: r'downloadManagerProvider', name: r'downloadManagerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
@ -189,8 +189,8 @@ final downloadManagerProvider = AutoDisposeNotifierProvider<DownloadManager,
allTransitiveDependencies: null, allTransitiveDependencies: null,
); );
typedef _$DownloadManager = AutoDisposeNotifier<core.AudiobookDownloadManager>; typedef _$DownloadManager = Notifier<core.AudiobookDownloadManager>;
String _$isItemDownloadingHash() => r'07e35ae25c096e9c9071667c6303a43f7b5e4cff'; String _$isItemDownloadingHash() => r'ea43c06393beec828134e08d5f896ddbcfbac8f0';
abstract class _$IsItemDownloading extends BuildlessAutoDisposeNotifier<bool> { abstract class _$IsItemDownloading extends BuildlessAutoDisposeNotifier<bool> {
late final String id; late final String id;
@ -332,37 +332,39 @@ class _IsItemDownloadingProviderElement
String get id => (origin as IsItemDownloadingProvider).id; String get id => (origin as IsItemDownloadingProvider).id;
} }
String _$downloadProgressHash() => r'1677f45191181765f6efebdb74206a438a47a4b7'; String _$itemDownloadProgressHash() =>
r'd007c55c6e2e4b992069d0306df8a600225d8598';
abstract class _$DownloadProgress extends BuildlessNotifier<double> { abstract class _$ItemDownloadProgress
extends BuildlessAutoDisposeAsyncNotifier<double?> {
late final String id; late final String id;
double build( FutureOr<double?> build(
String id, String id,
); );
} }
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
@ProviderFor(DownloadProgress) @ProviderFor(ItemDownloadProgress)
const downloadProgressProvider = DownloadProgressFamily(); const itemDownloadProgressProvider = ItemDownloadProgressFamily();
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
class DownloadProgressFamily extends Family<double> { class ItemDownloadProgressFamily extends Family<AsyncValue<double?>> {
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
const DownloadProgressFamily(); const ItemDownloadProgressFamily();
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
DownloadProgressProvider call( ItemDownloadProgressProvider call(
String id, String id,
) { ) {
return DownloadProgressProvider( return ItemDownloadProgressProvider(
id, id,
); );
} }
@override @override
DownloadProgressProvider getProviderOverride( ItemDownloadProgressProvider getProviderOverride(
covariant DownloadProgressProvider provider, covariant ItemDownloadProgressProvider provider,
) { ) {
return call( return call(
provider.id, provider.id,
@ -381,30 +383,30 @@ class DownloadProgressFamily extends Family<double> {
_allTransitiveDependencies; _allTransitiveDependencies;
@override @override
String? get name => r'downloadProgressProvider'; String? get name => r'itemDownloadProgressProvider';
} }
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
class DownloadProgressProvider class ItemDownloadProgressProvider extends AutoDisposeAsyncNotifierProviderImpl<
extends NotifierProviderImpl<DownloadProgress, double> { ItemDownloadProgress, double?> {
/// See also [DownloadProgress]. /// See also [ItemDownloadProgress].
DownloadProgressProvider( ItemDownloadProgressProvider(
String id, String id,
) : this._internal( ) : this._internal(
() => DownloadProgress()..id = id, () => ItemDownloadProgress()..id = id,
from: downloadProgressProvider, from: itemDownloadProgressProvider,
name: r'downloadProgressProvider', name: r'itemDownloadProgressProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$downloadProgressHash, : _$itemDownloadProgressHash,
dependencies: DownloadProgressFamily._dependencies, dependencies: ItemDownloadProgressFamily._dependencies,
allTransitiveDependencies: allTransitiveDependencies:
DownloadProgressFamily._allTransitiveDependencies, ItemDownloadProgressFamily._allTransitiveDependencies,
id: id, id: id,
); );
DownloadProgressProvider._internal( ItemDownloadProgressProvider._internal(
super._createNotifier, { super._createNotifier, {
required super.name, required super.name,
required super.dependencies, required super.dependencies,
@ -417,8 +419,8 @@ class DownloadProgressProvider
final String id; final String id;
@override @override
double runNotifierBuild( FutureOr<double?> runNotifierBuild(
covariant DownloadProgress notifier, covariant ItemDownloadProgress notifier,
) { ) {
return notifier.build( return notifier.build(
id, id,
@ -426,10 +428,10 @@ class DownloadProgressProvider
} }
@override @override
Override overrideWith(DownloadProgress Function() create) { Override overrideWith(ItemDownloadProgress Function() create) {
return ProviderOverride( return ProviderOverride(
origin: this, origin: this,
override: DownloadProgressProvider._internal( override: ItemDownloadProgressProvider._internal(
() => create()..id = id, () => create()..id = id,
from: from, from: from,
name: null, name: null,
@ -442,13 +444,14 @@ class DownloadProgressProvider
} }
@override @override
NotifierProviderElement<DownloadProgress, double> createElement() { AutoDisposeAsyncNotifierProviderElement<ItemDownloadProgress, double?>
return _DownloadProgressProviderElement(this); createElement() {
return _ItemDownloadProgressProviderElement(this);
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is DownloadProgressProvider && other.id == id; return other is ItemDownloadProgressProvider && other.id == id;
} }
@override @override
@ -460,23 +463,23 @@ class DownloadProgressProvider
} }
} }
mixin DownloadProgressRef on NotifierProviderRef<double> { mixin ItemDownloadProgressRef on AutoDisposeAsyncNotifierProviderRef<double?> {
/// The parameter `id` of this provider. /// The parameter `id` of this provider.
String get id; String get id;
} }
class _DownloadProgressProviderElement class _ItemDownloadProgressProviderElement
extends NotifierProviderElement<DownloadProgress, double> extends AutoDisposeAsyncNotifierProviderElement<ItemDownloadProgress,
with DownloadProgressRef { double?> with ItemDownloadProgressRef {
_DownloadProgressProviderElement(super.provider); _ItemDownloadProgressProviderElement(super.provider);
@override @override
String get id => (origin as DownloadProgressProvider).id; String get id => (origin as ItemDownloadProgressProvider).id;
} }
String _$downloadStatusHash() => r'3f2bf4e7fb01b9329d31f4797537a307aff70397'; String _$isItemDownloadedHash() => r'9bb7ba28bdb73e1ba706e849fedc9c7bd67f4b67';
abstract class _$DownloadStatus abstract class _$IsItemDownloaded
extends BuildlessAutoDisposeAsyncNotifier<bool> { extends BuildlessAutoDisposeAsyncNotifier<bool> {
late final LibraryItemExpanded item; late final LibraryItemExpanded item;
@ -485,27 +488,27 @@ abstract class _$DownloadStatus
); );
} }
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
@ProviderFor(DownloadStatus) @ProviderFor(IsItemDownloaded)
const downloadStatusProvider = DownloadStatusFamily(); const isItemDownloadedProvider = IsItemDownloadedFamily();
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
class DownloadStatusFamily extends Family<AsyncValue<bool>> { class IsItemDownloadedFamily extends Family<AsyncValue<bool>> {
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
const DownloadStatusFamily(); const IsItemDownloadedFamily();
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
DownloadStatusProvider call( IsItemDownloadedProvider call(
LibraryItemExpanded item, LibraryItemExpanded item,
) { ) {
return DownloadStatusProvider( return IsItemDownloadedProvider(
item, item,
); );
} }
@override @override
DownloadStatusProvider getProviderOverride( IsItemDownloadedProvider getProviderOverride(
covariant DownloadStatusProvider provider, covariant IsItemDownloadedProvider provider,
) { ) {
return call( return call(
provider.item, provider.item,
@ -524,30 +527,30 @@ class DownloadStatusFamily extends Family<AsyncValue<bool>> {
_allTransitiveDependencies; _allTransitiveDependencies;
@override @override
String? get name => r'downloadStatusProvider'; String? get name => r'isItemDownloadedProvider';
} }
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
class DownloadStatusProvider class IsItemDownloadedProvider
extends AutoDisposeAsyncNotifierProviderImpl<DownloadStatus, bool> { extends AutoDisposeAsyncNotifierProviderImpl<IsItemDownloaded, bool> {
/// See also [DownloadStatus]. /// See also [IsItemDownloaded].
DownloadStatusProvider( IsItemDownloadedProvider(
LibraryItemExpanded item, LibraryItemExpanded item,
) : this._internal( ) : this._internal(
() => DownloadStatus()..item = item, () => IsItemDownloaded()..item = item,
from: downloadStatusProvider, from: isItemDownloadedProvider,
name: r'downloadStatusProvider', name: r'isItemDownloadedProvider',
debugGetCreateSourceHash: debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') const bool.fromEnvironment('dart.vm.product')
? null ? null
: _$downloadStatusHash, : _$isItemDownloadedHash,
dependencies: DownloadStatusFamily._dependencies, dependencies: IsItemDownloadedFamily._dependencies,
allTransitiveDependencies: allTransitiveDependencies:
DownloadStatusFamily._allTransitiveDependencies, IsItemDownloadedFamily._allTransitiveDependencies,
item: item, item: item,
); );
DownloadStatusProvider._internal( IsItemDownloadedProvider._internal(
super._createNotifier, { super._createNotifier, {
required super.name, required super.name,
required super.dependencies, required super.dependencies,
@ -561,7 +564,7 @@ class DownloadStatusProvider
@override @override
FutureOr<bool> runNotifierBuild( FutureOr<bool> runNotifierBuild(
covariant DownloadStatus notifier, covariant IsItemDownloaded notifier,
) { ) {
return notifier.build( return notifier.build(
item, item,
@ -569,10 +572,10 @@ class DownloadStatusProvider
} }
@override @override
Override overrideWith(DownloadStatus Function() create) { Override overrideWith(IsItemDownloaded Function() create) {
return ProviderOverride( return ProviderOverride(
origin: this, origin: this,
override: DownloadStatusProvider._internal( override: IsItemDownloadedProvider._internal(
() => create()..item = item, () => create()..item = item,
from: from, from: from,
name: null, name: null,
@ -585,14 +588,14 @@ class DownloadStatusProvider
} }
@override @override
AutoDisposeAsyncNotifierProviderElement<DownloadStatus, bool> AutoDisposeAsyncNotifierProviderElement<IsItemDownloaded, bool>
createElement() { createElement() {
return _DownloadStatusProviderElement(this); return _IsItemDownloadedProviderElement(this);
} }
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
return other is DownloadStatusProvider && other.item == item; return other is IsItemDownloadedProvider && other.item == item;
} }
@override @override
@ -604,18 +607,18 @@ class DownloadStatusProvider
} }
} }
mixin DownloadStatusRef on AutoDisposeAsyncNotifierProviderRef<bool> { mixin IsItemDownloadedRef on AutoDisposeAsyncNotifierProviderRef<bool> {
/// The parameter `item` of this provider. /// The parameter `item` of this provider.
LibraryItemExpanded get item; LibraryItemExpanded get item;
} }
class _DownloadStatusProviderElement class _IsItemDownloadedProviderElement
extends AutoDisposeAsyncNotifierProviderElement<DownloadStatus, bool> extends AutoDisposeAsyncNotifierProviderElement<IsItemDownloaded, bool>
with DownloadStatusRef { with IsItemDownloadedRef {
_DownloadStatusProviderElement(super.provider); _IsItemDownloadedProviderElement(super.provider);
@override @override
LibraryItemExpanded get item => (origin as DownloadStatusProvider).item; LibraryItemExpanded get item => (origin as IsItemDownloadedProvider).item;
} }
// ignore_for_file: type=lint // ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member

View file

@ -1,7 +1,6 @@
import 'package:background_downloader/background_downloader.dart'; import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk; import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk;
@ -11,9 +10,9 @@ import 'package:vaani/features/downloads/providers/download_manager.dart'
show show
downloadHistoryProvider, downloadHistoryProvider,
downloadManagerProvider, downloadManagerProvider,
downloadProgressProvider, isItemDownloadedProvider,
downloadStatusProvider,
isItemDownloadingProvider, isItemDownloadingProvider,
itemDownloadProgressProvider,
simpleDownloadManagerProvider; simpleDownloadManagerProvider;
import 'package:vaani/features/item_viewer/view/library_item_page.dart'; import 'package:vaani/features/item_viewer/view/library_item_page.dart';
import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart'; import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart';
@ -214,12 +213,13 @@ class LibItemDownloadButton extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final isItemDownloaded = ref.watch(downloadStatusProvider(item)); final isItemDownloaded = ref.watch(isItemDownloadedProvider(item));
if (isItemDownloaded.valueOrNull ?? false) {
return AlreadyItemDownloadedButton(item: item);
}
final isItemDownloading = ref.watch(isItemDownloadingProvider(item.id)); final isItemDownloading = ref.watch(isItemDownloadingProvider(item.id));
return isItemDownloaded.valueOrNull ?? false return isItemDownloading
? AlreadyItemDownloadedButton(item: item)
: isItemDownloading
? ItemCurrentlyInDownloadQueue( ? ItemCurrentlyInDownloadQueue(
item: item, item: item,
) )
@ -248,9 +248,14 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final progress = ref.watch(downloadProgressProvider(item.id)); final progress =
final updatesStream = useStream(ref.watch(downloadManagerProvider).taskUpdateStream); ref.watch(itemDownloadProgressProvider(item.id)).valueOrNull;
if (progress == 1) {
return AlreadyItemDownloadedButton(item: item);
}
const shimmerDuration = Duration(milliseconds: 1000);
return Stack( return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -260,19 +265,22 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
), ),
const Icon( const Icon(
Icons.download, Icons.download,
// color: Theme.of(context).progressIndicatorTheme.color,
) )
.animate( .animate(
onPlay: (controller) => controller.repeat(), onPlay: (controller) => controller.repeat(),
) )
.fade( .fade(
begin: 0.5, duration: shimmerDuration,
end: 1, end: 1,
duration: 1.seconds, begin: 0.6,
curve: Curves.linearToEaseOut,
) )
.fade( .fade(
duration: shimmerDuration,
end: 0.7,
begin: 1, begin: 1,
end: 0.5, curve: Curves.easeInToLinear,
duration: 1.seconds,
), ),
], ],
); );
@ -520,7 +528,7 @@ Future<void> libraryItemPlayButtonOnPressed({
final downloadManager = ref.watch(simpleDownloadManagerProvider); final downloadManager = ref.watch(simpleDownloadManagerProvider);
final libItem = final libItem =
await ref.read(libraryItemProvider(book.libraryItemId).future); await ref.read(libraryItemProvider(book.libraryItemId).future);
final downloadedUris = await downloadManager.getDownloadedFiles(libItem); final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
setSourceFuture = player.setSourceAudiobook( setSourceFuture = player.setSourceAudiobook(
book, book,
initialPosition: userMediaProgress?.currentTime, initialPosition: userMediaProgress?.currentTime,

View file

@ -0,0 +1,10 @@
import 'package:shelfsdk/audiobookshelf_api.dart';
extension TotalSize on LibraryItemExpanded {
int get totalSize {
return libraryFiles.fold(
0,
(previousValue, element) => previousValue + element.metadata.size,
);
}
}