mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-07-07 01:41:33 +00:00
feat: add total size calculation for library items and improve download manager functionality
This commit is contained in:
parent
30f87c422b
commit
b0cc1ff8cb
5 changed files with 190 additions and 196 deletions
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
10
lib/shared/extensions/item_files.dart
Normal file
10
lib/shared/extensions/item_files.dart
Normal 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue