From c24541f1cd851091e281caf902ffc259fe7b0fbc Mon Sep 17 00:00:00 2001 From: Dr-Blank <64108942+Dr-Blank@users.noreply.github.com> Date: Tue, 20 Aug 2024 08:36:39 -0400 Subject: [PATCH] downloads and offline playback --- .vscode/tasks.json | 31 ++ android/app/src/main/AndroidManifest.xml | 1 + assets/icon/logo.png | Bin 0 -> 27345 bytes lib/api/api_provider.g.dart | 2 +- lib/api/authenticated_user_provider.g.dart | 2 +- lib/api/image_provider.g.dart | 2 +- lib/api/library_item_provider.g.dart | 2 +- .../downloads/core/download_manager.dart | 185 +++++++++++ .../downloads/providers/download_manager.dart | 51 +++ .../providers/download_manager.g.dart | 306 ++++++++++++++++++ .../downloads/view/downloads_page.dart | 51 +++ .../view/library_item_actions.dart | 252 ++++++++++++++- .../item_viewer/view/library_item_page.dart | 5 + .../view/library_browser_page.dart | 50 +++ .../player/core/audiobook_player.dart | 50 ++- .../player/providers/audiobook_player.dart | 15 +- .../player/providers/audiobook_player.g.dart | 12 +- .../player/providers/player_form.dart | 1 + .../player/view/audiobook_player.dart | 2 +- .../player/view/player_when_expanded.dart | 4 + .../widgets/player_speed_adjust_button.dart | 12 +- .../providers/sleep_timer_provider.dart | 3 +- .../providers/sleep_timer_provider.g.dart | 2 +- lib/main.dart | 7 + lib/pages/navigation.dart | 32 -- lib/router/constants.dart | 13 + lib/router/router.dart | 25 +- lib/router/scaffold_with_nav_bar.dart | 15 +- lib/settings/api_settings_provider.g.dart | 2 +- lib/settings/app_settings_provider.dart | 32 +- lib/settings/app_settings_provider.g.dart | 2 +- lib/settings/models/app_settings.dart | 18 +- lib/settings/models/app_settings.freezed.dart | 304 ++++++++++++++++- lib/settings/models/app_settings.g.dart | 30 +- lib/settings/view/app_settings_page.dart | 148 ++++++++- lib/shared/extensions/model_conversions.dart | 28 ++ lib/theme/theme_from_cover_provider.g.dart | 2 +- windows/runner/resources/app_icon.ico | Bin 33772 -> 199810 bytes 38 files changed, 1590 insertions(+), 109 deletions(-) create mode 100644 assets/icon/logo.png create mode 100644 lib/features/downloads/core/download_manager.dart create mode 100644 lib/features/downloads/providers/download_manager.dart create mode 100644 lib/features/downloads/providers/download_manager.g.dart create mode 100644 lib/features/downloads/view/downloads_page.dart create mode 100644 lib/features/library_browser/view/library_browser_page.dart delete mode 100644 lib/pages/navigation.dart diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e16f58e..df7e1c0 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -32,6 +32,37 @@ "message": 4 } } + }, + // flutter build apk --release + { + "icon": { "id": "package", "color": "terminal.ansiGreen" }, + "label": "flutter build release APK", + "type": "shell", + "command": "flutter build apk --release", + "group": { + "kind": "none", + "isDefault": false + }, + "detail": "Building APK in release mode", + "presentation": { + "revealProblems": "onProblem", + "reveal": "always", + "panel": "dedicated" + }, + "runOptions": { + "instanceLimit": 1 + }, + "problemMatcher": { + "owner": "dart", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "message": 4 + } + } } ] } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index cd911ca..62d44d0 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + S{^`|4P*VCn(mx++f8S8vqyqbtQQtKl6Pv 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 literal 0 HcmV?d00001 diff --git a/lib/api/api_provider.g.dart b/lib/api/api_provider.g.dart index eb91f78..cc754a7 100644 --- a/lib/api/api_provider.g.dart +++ b/lib/api/api_provider.g.dart @@ -361,7 +361,7 @@ final meProvider = AutoDisposeFutureProvider.internal( ); typedef MeRef = AutoDisposeFutureProviderRef; -String _$personalizedViewHash() => r'2e70fe2bfc766a963f7a8e94211ad50d959fbaa2'; +String _$personalizedViewHash() => r'dada8d72845ffd516f731f88193941f7ebdd47ed'; /// fetch the personalized view /// diff --git a/lib/api/authenticated_user_provider.g.dart b/lib/api/authenticated_user_provider.g.dart index 1ede072..cd41f52 100644 --- a/lib/api/authenticated_user_provider.g.dart +++ b/lib/api/authenticated_user_provider.g.dart @@ -6,7 +6,7 @@ part of 'authenticated_user_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$authenticatedUserHash() => r'5702fb6ab1e83129d57c89ef02a65c5910f2a076'; +String _$authenticatedUserHash() => r'8578d7fda1755ecacce6853076da4149e4ebe3e7'; /// provides with a set of authenticated users /// diff --git a/lib/api/image_provider.g.dart b/lib/api/image_provider.g.dart index 87240f0..74f9d8e 100644 --- a/lib/api/image_provider.g.dart +++ b/lib/api/image_provider.g.dart @@ -6,7 +6,7 @@ part of 'image_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$coverImageHash() => r'fa97592576b5450053066fcd644f2b5c30d3a5bc'; +String _$coverImageHash() => r'702afafa217dfcbb290837caf21cc1ef54defd55'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/api/library_item_provider.g.dart b/lib/api/library_item_provider.g.dart index 68b1586..80370c6 100644 --- a/lib/api/library_item_provider.g.dart +++ b/lib/api/library_item_provider.g.dart @@ -6,7 +6,7 @@ part of 'library_item_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$libraryItemHash() => r'4c9a9e6d6700c7c76fbf56ecf5c0873155d5061a'; +String _$libraryItemHash() => r'fa3f8309349c5b1b777f1bc919616e51c3f5b520'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/features/downloads/core/download_manager.dart b/lib/features/downloads/core/download_manager.dart new file mode 100644 index 0000000..5caba14 --- /dev/null +++ b/lib/features/downloads/core/download_manager.dart @@ -0,0 +1,185 @@ +// download manager to handle download tasks of files + +import 'dart:io'; + +import 'package:background_downloader/background_downloader.dart'; +import 'package:logging/logging.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shelfsdk/audiobookshelf_api.dart'; +import 'package:whispering_pages/shared/extensions/model_conversions.dart'; + +final _logger = Logger('AudiobookDownloadManager'); +final tq = MemoryTaskQueue(); +const downloadDirectory = BaseDirectory.applicationSupport; + +class AudiobookDownloadManager { + // takes in the baseUrl and the token + AudiobookDownloadManager({ + required this.baseUrl, + required this.token, + this.requiresWiFi = true, + this.retries = 0, + this.allowPause = false, + + // /// The maximum number of concurrent tasks to run at any given time. + // int maxConcurrent = 3, + + // /// The maximum number of concurrent tasks to run for the same host. + // int maxConcurrentByHost = 2, + + // /// The maximum number of concurrent tasks to run for the same group. + // int maxConcurrentByGroup = 3, + }) { + // initialize the download manager + + FileDownloader().addTaskQueue(tq); + + _logger.fine('initialized with baseUrl: $baseUrl, token: $token'); + _logger.fine( + 'requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause', + ); + } + + // the base url for the audio files + final String baseUrl; + + // the authentication token to access the [AudioTrack.contentUrl] + final String token; + + // whether to download only on wifi + final bool requiresWiFi; + + // the number of retries to attempt + final int retries; + + // whether to allow pausing of downloads + final bool allowPause; + + // final List _downloadTasks = []; + + Future queueAudioBookDownload(LibraryItemExpanded item) async { + _logger.info('queuing download for item: ${item.toJson()}'); + // create a download task for each file in the item + final directory = await getApplicationSupportDirectory(); + for (final file in item.libraryFiles) { + // check if the file is already downloaded + if (isFileDownloaded( + constructFilePath(directory, item, file), + )) { + _logger.info('file already downloaded: ${file.metadata.filename}'); + continue; + } + + final task = DownloadTask( + taskId: file.ino, + url: file.url(baseUrl, item.id, token).toString(), + directory: item.relPath, + filename: file.metadata.filename, + requiresWiFi: requiresWiFi, + retries: retries, + allowPause: allowPause, + group: item.id, + baseDirectory: downloadDirectory, + // metaData: token + ); + // _downloadTasks.add(task); + tq.add(task); + _logger.info('queued task: ${task.toJson()}'); + } + + FileDownloader().registerCallbacks( + group: item.id, + taskProgressCallback: (update) { + _logger.info('Group: ${item.id}, Progress Update: ${update.progress}'); + }, + taskStatusCallback: (update) { + 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) { + _logger.info('Group: ${item.id}, Task: ${task.toJson()}'); + }, + ); + } + + String constructFilePath( + Directory directory, + LibraryItemExpanded item, + LibraryFile file, + ) => + '${directory.path}/${item.relPath}/${file.metadata.filename}'; + + // void startDownload() { + // for (final task in _downloadTasks) { + // _logger.fine('enqueuing task: $task'); + // FileDownloader().enqueue(task); + // } + // } + + void dispose() { + // tq.removeAll(); + _logger.fine('disposed'); + } + + bool isFileDownloaded(String filePath) { + // Check if the file exists + final fileExists = File(filePath).existsSync(); + + return fileExists; + } + + Future isItemDownloaded(LibraryItemExpanded item) async { + final directory = await getApplicationSupportDirectory(); + for (final file in item.libraryFiles) { + if (!isFileDownloaded(constructFilePath(directory, item, file))) { + _logger.info('file not downloaded: ${file.metadata.filename}'); + return false; + } + } + + return true; + } + + Future deleteDownloadedItem(LibraryItemExpanded item) async { + final directory = await getApplicationSupportDirectory(); + for (final file in item.libraryFiles) { + final filePath = constructFilePath(directory, item, file); + if (isFileDownloaded(filePath)) { + File(filePath).deleteSync(); + } + _logger.info('deleted file: $filePath'); + } + } + + Future> getDownloadedFiles(LibraryItemExpanded item) async { + final directory = await getApplicationSupportDirectory(); + final files = []; + for (final file in item.libraryFiles) { + final filePath = constructFilePath(directory, item, file); + if (isFileDownloaded(filePath)) { + files.add(Uri.file(filePath)); + } + } + + return files; + } +} + +Future initDownloadManager() async { + // initialize the download manager + var fileDownloader = FileDownloader(); + fileDownloader.configureNotification( + running: const TaskNotification('Downloading', 'file: {filename}'), + progressBar: true, + ); + await fileDownloader.trackTasks(); +} diff --git a/lib/features/downloads/providers/download_manager.dart b/lib/features/downloads/providers/download_manager.dart new file mode 100644 index 0000000..5db75c7 --- /dev/null +++ b/lib/features/downloads/providers/download_manager.dart @@ -0,0 +1,51 @@ +import 'package:background_downloader/background_downloader.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:shelfsdk/audiobookshelf_api.dart'; +import 'package:whispering_pages/api/api_provider.dart'; +import 'package:whispering_pages/features/downloads/core/download_manager.dart' + as core; +import 'package:whispering_pages/settings/app_settings_provider.dart'; + +part 'download_manager.g.dart'; + +@Riverpod(keepAlive: true) +class SimpleDownloadManager extends _$SimpleDownloadManager { + @override + core.AudiobookDownloadManager build() { + final api = ref.watch(authenticatedApiProvider); + final downloadSettings = ref.watch(appSettingsProvider).downloadSettings; + + final manager = core.AudiobookDownloadManager( + baseUrl: api.baseUrl.toString(), + token: api.token!, + requiresWiFi: downloadSettings.requiresWiFi, + retries: downloadSettings.retries, + allowPause: downloadSettings.allowPause, + ); + core.tq.maxConcurrent = downloadSettings.maxConcurrent; + core.tq.maxConcurrentByHost = downloadSettings.maxConcurrentByHost; + core.tq.maxConcurrentByGroup = downloadSettings.maxConcurrentByGroup; + ref.onDispose(() { + manager.dispose(); + }); + + return manager; + } +} + +@riverpod +FutureOr> downloadHistory( + DownloadHistoryRef ref, { + String? group, +}) async { + return await FileDownloader().database.allRecords(group: group); +} + +@Riverpod(keepAlive: false) +FutureOr downloadStatus( + DownloadStatusRef ref, + LibraryItemExpanded item, +) async { + final manager = ref.read(simpleDownloadManagerProvider); + return manager.isItemDownloaded(item); +} diff --git a/lib/features/downloads/providers/download_manager.g.dart b/lib/features/downloads/providers/download_manager.g.dart new file mode 100644 index 0000000..7a21240 --- /dev/null +++ b/lib/features/downloads/providers/download_manager.g.dart @@ -0,0 +1,306 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'download_manager.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$downloadHistoryHash() => r'76c449e8abfa61d57566991686f534a06dc7fef7'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [downloadHistory]. +@ProviderFor(downloadHistory) +const downloadHistoryProvider = DownloadHistoryFamily(); + +/// See also [downloadHistory]. +class DownloadHistoryFamily extends Family>> { + /// See also [downloadHistory]. + const DownloadHistoryFamily(); + + /// See also [downloadHistory]. + DownloadHistoryProvider call({ + String? group, + }) { + return DownloadHistoryProvider( + group: group, + ); + } + + @override + DownloadHistoryProvider getProviderOverride( + covariant DownloadHistoryProvider provider, + ) { + return call( + group: provider.group, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'downloadHistoryProvider'; +} + +/// See also [downloadHistory]. +class DownloadHistoryProvider + extends AutoDisposeFutureProvider> { + /// See also [downloadHistory]. + DownloadHistoryProvider({ + String? group, + }) : this._internal( + (ref) => downloadHistory( + ref as DownloadHistoryRef, + group: group, + ), + from: downloadHistoryProvider, + name: r'downloadHistoryProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$downloadHistoryHash, + dependencies: DownloadHistoryFamily._dependencies, + allTransitiveDependencies: + DownloadHistoryFamily._allTransitiveDependencies, + group: group, + ); + + DownloadHistoryProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.group, + }) : super.internal(); + + final String? group; + + @override + Override overrideWith( + FutureOr> Function(DownloadHistoryRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: DownloadHistoryProvider._internal( + (ref) => create(ref as DownloadHistoryRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + group: group, + ), + ); + } + + @override + AutoDisposeFutureProviderElement> createElement() { + return _DownloadHistoryProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is DownloadHistoryProvider && other.group == group; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, group.hashCode); + + return _SystemHash.finish(hash); + } +} + +mixin DownloadHistoryRef on AutoDisposeFutureProviderRef> { + /// The parameter `group` of this provider. + String? get group; +} + +class _DownloadHistoryProviderElement + extends AutoDisposeFutureProviderElement> + with DownloadHistoryRef { + _DownloadHistoryProviderElement(super.provider); + + @override + String? get group => (origin as DownloadHistoryProvider).group; +} + +String _$downloadStatusHash() => r'f37b4678d3c2a7c6e985b0149d72ea0f9b1b42ca'; + +/// See also [downloadStatus]. +@ProviderFor(downloadStatus) +const downloadStatusProvider = DownloadStatusFamily(); + +/// See also [downloadStatus]. +class DownloadStatusFamily extends Family> { + /// See also [downloadStatus]. + const DownloadStatusFamily(); + + /// See also [downloadStatus]. + DownloadStatusProvider call( + LibraryItemExpanded item, + ) { + return DownloadStatusProvider( + item, + ); + } + + @override + DownloadStatusProvider getProviderOverride( + covariant DownloadStatusProvider provider, + ) { + return call( + provider.item, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'downloadStatusProvider'; +} + +/// See also [downloadStatus]. +class DownloadStatusProvider extends AutoDisposeFutureProvider { + /// See also [downloadStatus]. + DownloadStatusProvider( + LibraryItemExpanded item, + ) : this._internal( + (ref) => downloadStatus( + ref as DownloadStatusRef, + item, + ), + from: downloadStatusProvider, + name: r'downloadStatusProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$downloadStatusHash, + dependencies: DownloadStatusFamily._dependencies, + allTransitiveDependencies: + DownloadStatusFamily._allTransitiveDependencies, + item: item, + ); + + DownloadStatusProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.item, + }) : super.internal(); + + final LibraryItemExpanded item; + + @override + Override overrideWith( + FutureOr Function(DownloadStatusRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: DownloadStatusProvider._internal( + (ref) => create(ref as DownloadStatusRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + item: item, + ), + ); + } + + @override + AutoDisposeFutureProviderElement createElement() { + return _DownloadStatusProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is DownloadStatusProvider && other.item == item; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, item.hashCode); + + return _SystemHash.finish(hash); + } +} + +mixin DownloadStatusRef on AutoDisposeFutureProviderRef { + /// The parameter `item` of this provider. + LibraryItemExpanded get item; +} + +class _DownloadStatusProviderElement + extends AutoDisposeFutureProviderElement with DownloadStatusRef { + _DownloadStatusProviderElement(super.provider); + + @override + LibraryItemExpanded get item => (origin as DownloadStatusProvider).item; +} + +String _$simpleDownloadManagerHash() => + r'cec95717c86e422f88f78aa014d29e800e5a2089'; + +/// See also [SimpleDownloadManager]. +@ProviderFor(SimpleDownloadManager) +final simpleDownloadManagerProvider = NotifierProvider.internal( + SimpleDownloadManager.new, + name: r'simpleDownloadManagerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$simpleDownloadManagerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$SimpleDownloadManager = 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 diff --git a/lib/features/downloads/view/downloads_page.dart b/lib/features/downloads/view/downloads_page.dart new file mode 100644 index 0000000..9a68186 --- /dev/null +++ b/lib/features/downloads/view/downloads_page.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:whispering_pages/features/downloads/providers/download_manager.dart'; + +class DownloadsPage extends HookConsumerWidget { + const DownloadsPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final manager = ref.read(simpleDownloadManagerProvider); + final downloadHistory = ref.watch(downloadHistoryProvider()); + + return Scaffold( + appBar: AppBar( + title: const Text('Downloads'), + backgroundColor: Colors.transparent, + ), + body: Center( + // history of downloads + child: downloadHistory.when( + data: (records) { + // each group is one list tile, which contains the files downloaded + final uniqueGroups = records.map((e) => e.group).toSet(); + return ListView.builder( + itemCount: uniqueGroups.length, + itemBuilder: (context, index) { + final group = uniqueGroups.elementAt(index); + final groupRecords = records.where((e) => e.group == group); + return ExpansionTile( + title: Text(group ?? 'No Group'), + children: groupRecords + .map( + (e) => ListTile( + title: Text('${e.task.directory}/${e.task.filename}'), + subtitle: Text(e.task.creationTime.toString()), + ), + ) + .toList(), + ); + }, + ); + }, + loading: () => const CircularProgressIndicator(), + error: (error, stackTrace) { + return Text('Error: $error'); + }, + ), + ), + ); + } +} diff --git a/lib/features/item_viewer/view/library_item_actions.dart b/lib/features/item_viewer/view/library_item_actions.dart index 61b52a0..f9a25fc 100644 --- a/lib/features/item_viewer/view/library_item_actions.dart +++ b/lib/features/item_viewer/view/library_item_actions.dart @@ -1,9 +1,18 @@ +import 'package:background_downloader/background_downloader.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:shelfsdk/audiobookshelf_api.dart' as shelfsdk; +import 'package:whispering_pages/api/library_item_provider.dart'; import 'package:whispering_pages/constants/hero_tag_conventions.dart'; +import 'package:whispering_pages/features/downloads/providers/download_manager.dart' + show + downloadHistoryProvider, + downloadStatusProvider, + simpleDownloadManagerProvider; import 'package:whispering_pages/features/item_viewer/view/library_item_page.dart'; import 'package:whispering_pages/features/player/providers/audiobook_player.dart'; +import 'package:whispering_pages/main.dart'; import 'package:whispering_pages/settings/app_settings_provider.dart'; import 'package:whispering_pages/shared/extensions/model_conversions.dart'; @@ -19,7 +28,10 @@ class LibraryItemActions extends HookConsumerWidget { late final shelfsdk.BookExpanded book; @override Widget build(BuildContext context, WidgetRef ref) { - final player = ref.read(audiobookPlayerProvider); + final manager = ref.read(simpleDownloadManagerProvider); + final downloadHistory = ref.watch(downloadHistoryProvider(group: item.id)); + final isItemDownloaded = ref.watch(downloadStatusProvider(item)); + return Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0), child: Row( @@ -55,16 +67,150 @@ class LibraryItemActions extends HookConsumerWidget { onPressed: () {}, icon: const Icon(Icons.share_rounded), ), - // download button - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.download_rounded, - ), + // check if the book is downloaded using manager.isItemDownloaded + isItemDownloaded.when( + data: (isDownloaded) { + if (isDownloaded) { + // already downloaded button + return IconButton( + onPressed: () { + appLogger + .fine('Pressed already downloaded button'); + // manager.openDownloadedFile(item); + // open popup menu to open or delete the file + showModalBottomSheet( + // useRootNavigator: true, + context: context, + builder: (context) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + ), + child: DownloadSheet( + item: item, + ), + ); + }, + ); + }, + icon: const Icon( + Icons.download_done_rounded, + ), + ); + } + // download button + return IconButton( + onPressed: () { + appLogger.fine('Pressed download button'); + manager.queueAudioBookDownload(item); + }, + icon: const Icon( + Icons.download_rounded, + ), + ); + }, + loading: () => const CircularProgressIndicator(), + error: (error, stackTrace) { + return IconButton( + onPressed: () { + appLogger.warning( + 'Error checking download status: $error', + ); + }, + icon: const Icon(Icons.error_rounded), + ); + }, ), // more button IconButton( - onPressed: () {}, + onPressed: () { + // show the bottom sheet with download history + showModalBottomSheet( + context: context, + builder: (context) { + return downloadHistory.when( + data: (downloadHistory) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: ListView.builder( + itemCount: downloadHistory.length, + itemBuilder: (context, index) { + final record = downloadHistory[index]; + return ListTile( + title: Text(record.task.filename), + subtitle: Text( + '${record.task.directory}/${record.task.baseDirectory}', + ), + trailing: const Icon( + Icons.open_in_new_rounded, + ), + onLongPress: () { + // show the delete dialog + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Delete'), + content: Text( + 'Are you sure you want to delete ${record.task.filename}?', + ), + actions: [ + TextButton( + onPressed: () { + // delete the file + FileDownloader() + .database + .deleteRecordWithId( + record + .task.taskId, + ); + Navigator.pop(context); + }, + child: const Text('Yes'), + ), + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: const Text('No'), + ), + ], + ); + }, + ); + }, + onTap: () async { + // open the file location + final didOpen = + await FileDownloader().openFile( + task: record.task, + ); + + if (!didOpen) { + appLogger.warning( + 'Failed to open file: ${record.task.filename} at ${record.task.directory}', + ); + return; + } + appLogger.fine( + 'Opened file: ${record.task.filename} at ${record.task.directory}', + ); + }, + ); + }, + ), + ); + }, + loading: () => const Center( + child: CircularProgressIndicator(), + ), + error: (error, stackTrace) => Center( + child: Text('Error: $error'), + ), + ); + }, + ); + }, icon: const Icon( Icons.more_vert_rounded, ), @@ -81,6 +227,84 @@ class LibraryItemActions extends HookConsumerWidget { } } +class DownloadSheet extends HookConsumerWidget { + const DownloadSheet({ + super.key, + required this.item, + }); + + final shelfsdk.LibraryItemExpanded item; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final manager = ref.read(simpleDownloadManagerProvider); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // ListTile( + // title: const Text('Open'), + // onTap: () async { + // final didOpen = + // await FileDownloader().openFile( + // task: manager.getTaskForItem(item), + // ); + + // if (!didOpen) { + // appLogger.warning( + // 'Failed to open file: ${item.title}', + // ); + // return; + // } + // appLogger.fine( + // 'Opened file: ${item.title}', + // ); + // }, + // ), + ListTile( + title: const Text('Delete'), + leading: const Icon( + Icons.delete_rounded, + ), + onTap: () { + // show the delete dialog + showDialog( + useRootNavigator: false, + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Delete'), + content: Text( + 'Are you sure you want to delete ${item.media.metadata.title}?', + ), + actions: [ + TextButton( + onPressed: () { + // delete the file + manager.deleteDownloadedItem( + item, + ); + GoRouter.of(context).pop(); + }, + child: const Text('Yes'), + ), + TextButton( + onPressed: () { + GoRouter.of(context).pop(); + }, + child: const Text('No'), + ), + ], + ); + }, + ); + }, + ), + ], + ); + } +} + class _LibraryItemPlayButton extends HookConsumerWidget { const _LibraryItemPlayButton({ super.key, @@ -122,7 +346,10 @@ class _LibraryItemPlayButton extends HookConsumerWidget { return ElevatedButton.icon( onPressed: () => libraryItemPlayButtonOnPressed( - ref: ref, book: book, userMediaProgress: userMediaProgress), + ref: ref, + book: book, + userMediaProgress: userMediaProgress, + ), icon: Hero( tag: HeroTagPrefixes.libraryItemPlayButton + book.libraryItemId, child: DynamicItemPlayIcon( @@ -182,9 +409,14 @@ Future libraryItemPlayButtonOnPressed({ if (!isCurrentBookSetInPlayer) { debugPrint('Setting the book ${book.libraryItemId}'); debugPrint('Initial position: ${userMediaProgress?.currentTime}'); - await player.setSourceAudioBook( + final downloadManager = ref.watch(simpleDownloadManagerProvider); + final libItem = + await ref.read(libraryItemProvider(book.libraryItemId).future); + final downloadedUris = await downloadManager.getDownloadedFiles(libItem); + await player.setSourceAudiobook( book, initialPosition: userMediaProgress?.currentTime, + downloadedUris: downloadedUris, ); } else { debugPrint('Book was already set'); diff --git a/lib/features/item_viewer/view/library_item_page.dart b/lib/features/item_viewer/view/library_item_page.dart index a3e40de..44f688f 100644 --- a/lib/features/item_viewer/view/library_item_page.dart +++ b/lib/features/item_viewer/view/library_item_page.dart @@ -6,6 +6,7 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:whispering_pages/api/library_item_provider.dart'; import 'package:whispering_pages/features/item_viewer/view/library_item_sliver_app_bar.dart'; +import 'package:whispering_pages/features/player/providers/player_form.dart'; import 'package:whispering_pages/router/models/library_item_extras.dart'; import 'package:whispering_pages/settings/app_settings_provider.dart'; import 'package:whispering_pages/shared/extensions/model_conversions.dart'; @@ -101,6 +102,10 @@ class LibraryItemPage extends HookConsumerWidget { ) : const SizedBox.shrink(), ), + // a padding at the bottom to make sure the last item is not hidden by mini player + const SliverToBoxAdapter( + child: SizedBox(height: playerMinHeight), + ), ], ), ), diff --git a/lib/features/library_browser/view/library_browser_page.dart b/lib/features/library_browser/view/library_browser_page.dart new file mode 100644 index 0000000..40d46cf --- /dev/null +++ b/lib/features/library_browser/view/library_browser_page.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:whispering_pages/router/router.dart'; + +class LibraryBrowserPage extends HookConsumerWidget { + const LibraryBrowserPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar( + title: const Text('Library'), + backgroundColor: Colors.transparent, + ), + // a list redirecting to authors, genres, and series pages + body: ListView( + children: [ + ListTile( + title: const Text('Authors'), + leading: const Icon(Icons.person), + trailing: const Icon(Icons.chevron_right), + onTap: () {}, + ), + ListTile( + title: const Text('Genres'), + leading: const Icon(Icons.category), + trailing: const Icon(Icons.chevron_right), + onTap: () {}, + ), + ListTile( + title: const Text('Series'), + leading: const Icon(Icons.list), + trailing: const Icon(Icons.chevron_right), + onTap: () {}, + ), + // Downloads + ListTile( + title: const Text('Downloads'), + leading: const Icon(Icons.download), + trailing: const Icon(Icons.chevron_right), + onTap: () { + GoRouter.of(context).pushNamed(Routes.downloads.name); + }, + ), + ], + ), + ); + } +} diff --git a/lib/features/player/core/audiobook_player.dart b/lib/features/player/core/audiobook_player.dart index cc6c471..106a547 100644 --- a/lib/features/player/core/audiobook_player.dart +++ b/lib/features/player/core/audiobook_player.dart @@ -3,11 +3,14 @@ /// this is needed as audiobook can be a list of audio files instead of a single file library; -import 'package:flutter/foundation.dart'; +import 'package:collection/collection.dart'; import 'package:just_audio/just_audio.dart'; import 'package:just_audio_background/just_audio_background.dart'; +import 'package:logging/logging.dart'; import 'package:shelfsdk/audiobookshelf_api.dart'; +final _logger = Logger('AudiobookPlayer'); + /// returns the sum of the duration of all the previous tracks before the [index] Duration sumOfTracks(BookExpanded book, int? index) { // return 0 if index is less than 0 @@ -54,7 +57,7 @@ class AudiobookPlayer extends AudioPlayer { /// the [BookExpanded] being played /// - /// to set the book, use [setSourceAudioBook] + /// to set the book, use [setSourceAudiobook] BookExpanded? get book => _book; /// the authentication token to access the [AudioTrack.contentUrl] @@ -70,21 +73,24 @@ class AudiobookPlayer extends AudioPlayer { int? get availableTracks => _book?.tracks.length; /// sets the current [AudioTrack] as the source of the player - Future setSourceAudioBook( + Future setSourceAudiobook( BookExpanded? book, { bool preload = true, // int? initialIndex, Duration? initialPosition, + List? downloadedUris, + Uri? artworkUri, }) async { // if the book is null, stop the player if (book == null) { _book = null; + _logger.info('Book is null, stopping player'); return stop(); } // see if the book is the same as the current book if (_book == book) { - // if the book is the same, do nothing + _logger.info('Book is the same, doing nothing'); return; } // first stop the player and clear the source @@ -111,23 +117,29 @@ class AudiobookPlayer extends AudioPlayer { ConcatenatingAudioSource( useLazyPreparation: true, children: book.tracks.map((track) { + final retrievedUri = + _getUri(track, downloadedUris, baseUrl: baseUrl, token: token); + _logger.fine( + 'Setting source for track: ${track.title}, URI: $retrievedUri', + ); return AudioSource.uri( - Uri.parse('$baseUrl${track.contentUrl}?token=$token'), + retrievedUri, tag: MediaItem( // Specify a unique ID for each media item: id: book.libraryItemId + track.index.toString(), // Metadata to display in the notification: album: book.metadata.title, title: book.metadata.title ?? track.title, - artUri: Uri.parse( - '$baseUrl/api/items/${book.libraryItemId}/cover?token=$token&width=800', - ), + artUri: artworkUri ?? + Uri.parse( + '$baseUrl/api/items/${book.libraryItemId}/cover?token=$token&width=800', + ), ), ); }).toList(), ), ).catchError((error) { - debugPrint('AudiobookPlayer Error: $error'); + _logger.shout('Error: $error'); }); } @@ -176,7 +188,8 @@ class AudiobookPlayer extends AudioPlayer { if (_book == null) { return Duration.zero; } - return bufferedPosition + _book!.tracks[sequenceState!.currentIndex].startOffset; + return bufferedPosition + + _book!.tracks[sequenceState!.currentIndex].startOffset; } /// streams to override to suit the book instead of the current track @@ -237,3 +250,20 @@ class AudiobookPlayer extends AudioPlayer { ); } } + +Uri _getUri( + AudioTrack track, + List? downloadedUris, { + required Uri baseUrl, + required String token, +}) { + // check if the track is in the downloadedUris + final uri = downloadedUris?.firstWhereOrNull( + (element) { + return element.pathSegments.last == track.metadata?.filename; + }, + ); + + return uri ?? + Uri.parse('${baseUrl.toString()}${track.contentUrl}?token=$token'); +} diff --git a/lib/features/player/providers/audiobook_player.dart b/lib/features/player/providers/audiobook_player.dart index 593854b..ede5013 100644 --- a/lib/features/player/providers/audiobook_player.dart +++ b/lib/features/player/providers/audiobook_player.dart @@ -1,16 +1,16 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:whispering_pages/api/api_provider.dart'; import 'package:whispering_pages/features/player/core/audiobook_player.dart' - as abp; + as core; part 'audiobook_player.g.dart'; // @Riverpod(keepAlive: true) -// abp.AudiobookPlayer audiobookPlayer( +// core.AudiobookPlayer audiobookPlayer( // AudiobookPlayerRef ref, // ) { // final api = ref.watch(authenticatedApiProvider); -// final player = abp.AudiobookPlayer(api.token!, api.baseUrl); +// final player = core.AudiobookPlayer(api.token!, api.baseUrl); // ref.onDispose(player.dispose); @@ -24,9 +24,12 @@ const playerId = 'audiobook_player'; @Riverpod(keepAlive: true) class SimpleAudiobookPlayer extends _$SimpleAudiobookPlayer { @override - abp.AudiobookPlayer build() { + core.AudiobookPlayer build() { final api = ref.watch(authenticatedApiProvider); - final player = abp.AudiobookPlayer(api.token!, api.baseUrl); + final player = core.AudiobookPlayer( + api.token!, + api.baseUrl, + ); ref.onDispose(player.dispose); @@ -37,7 +40,7 @@ class SimpleAudiobookPlayer extends _$SimpleAudiobookPlayer { @Riverpod(keepAlive: true) class AudiobookPlayer extends _$AudiobookPlayer { @override - abp.AudiobookPlayer build() { + core.AudiobookPlayer build() { final player = ref.watch(simpleAudiobookPlayerProvider); ref.onDispose(player.dispose); diff --git a/lib/features/player/providers/audiobook_player.g.dart b/lib/features/player/providers/audiobook_player.g.dart index 1ff198c..849cde6 100644 --- a/lib/features/player/providers/audiobook_player.g.dart +++ b/lib/features/player/providers/audiobook_player.g.dart @@ -7,7 +7,7 @@ part of 'audiobook_player.dart'; // ************************************************************************** String _$simpleAudiobookPlayerHash() => - r'b65e6d779476a2c1fa38f617771bf997acb4f5b8'; + r'9e11ed2791d35e308f8cbe61a79a45cf51466ebb'; /// Simple because it doesn't rebuild when the player state changes /// it only rebuilds when the token changes @@ -15,7 +15,7 @@ String _$simpleAudiobookPlayerHash() => /// Copied from [SimpleAudiobookPlayer]. @ProviderFor(SimpleAudiobookPlayer) final simpleAudiobookPlayerProvider = - NotifierProvider.internal( + NotifierProvider.internal( SimpleAudiobookPlayer.new, name: r'simpleAudiobookPlayerProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -25,13 +25,13 @@ final simpleAudiobookPlayerProvider = allTransitiveDependencies: null, ); -typedef _$SimpleAudiobookPlayer = Notifier; -String _$audiobookPlayerHash() => r'38042d0c93034e6907677fdb614a9af1b9d636af'; +typedef _$SimpleAudiobookPlayer = Notifier; +String _$audiobookPlayerHash() => r'44394b1dbbf85eb19ef1f693717e8cbc15b768e5'; /// See also [AudiobookPlayer]. @ProviderFor(AudiobookPlayer) final audiobookPlayerProvider = - NotifierProvider.internal( + NotifierProvider.internal( AudiobookPlayer.new, name: r'audiobookPlayerProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -41,6 +41,6 @@ final audiobookPlayerProvider = allTransitiveDependencies: null, ); -typedef _$AudiobookPlayer = Notifier; +typedef _$AudiobookPlayer = 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 diff --git a/lib/features/player/providers/player_form.dart b/lib/features/player/providers/player_form.dart index 1b0c3f2..975f47f 100644 --- a/lib/features/player/providers/player_form.dart +++ b/lib/features/player/providers/player_form.dart @@ -9,6 +9,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'player_form.g.dart'; +/// The height of the player when it is minimized const double playerMinHeight = 70; // const miniplayerPercentageDeclaration = 0.2; diff --git a/lib/features/player/view/audiobook_player.dart b/lib/features/player/view/audiobook_player.dart index cfb6b5e..aa9e33f 100644 --- a/lib/features/player/view/audiobook_player.dart +++ b/lib/features/player/view/audiobook_player.dart @@ -98,7 +98,7 @@ class AudiobookPlayer extends HookConsumerWidget { // add a delay before closing the player // to allow the user to see the player closing Future.delayed(const Duration(milliseconds: 300), () { - player.setSourceAudioBook(null); + player.setSourceAudiobook(null); }); }, curve: Curves.easeOut, diff --git a/lib/features/player/view/player_when_expanded.dart b/lib/features/player/view/player_when_expanded.dart index a5fc097..fdcf4eb 100644 --- a/lib/features/player/view/player_when_expanded.dart +++ b/lib/features/player/view/player_when_expanded.dart @@ -17,6 +17,8 @@ import 'widgets/audiobook_player_seek_button.dart'; import 'widgets/audiobook_player_seek_chapter_button.dart'; import 'widgets/player_speed_adjust_button.dart'; +var pendingPlayerModals = 0; + class PlayerWhenExpanded extends HookConsumerWidget { const PlayerWhenExpanded({ super.key, @@ -270,6 +272,7 @@ class SleepTimerButton extends HookConsumerWidget { message: 'Sleep Timer', child: InkWell( onTap: () async { + pendingPlayerModals++; // show the sleep timer dialog final resultingDuration = await showDurationPicker( context: context, @@ -279,6 +282,7 @@ class SleepTimerButton extends HookConsumerWidget { .sleepTimerSettings .defaultDuration, ); + pendingPlayerModals--; if (resultingDuration != null) { // if 0 is selected, cancel the timer if (resultingDuration.inSeconds == 0) { diff --git a/lib/features/player/view/widgets/player_speed_adjust_button.dart b/lib/features/player/view/widgets/player_speed_adjust_button.dart index 3248967..7f8295e 100644 --- a/lib/features/player/view/widgets/player_speed_adjust_button.dart +++ b/lib/features/player/view/widgets/player_speed_adjust_button.dart @@ -1,8 +1,12 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:logging/logging.dart'; import 'package:whispering_pages/features/player/providers/audiobook_player.dart'; +import 'package:whispering_pages/features/player/view/player_when_expanded.dart'; import 'package:whispering_pages/features/player/view/widgets/speed_selector.dart'; +final _logger = Logger('PlayerSpeedAdjustButton'); + class PlayerSpeedAdjustButton extends HookConsumerWidget { const PlayerSpeedAdjustButton({ super.key, @@ -14,8 +18,10 @@ class PlayerSpeedAdjustButton extends HookConsumerWidget { final notifier = ref.watch(audiobookPlayerProvider.notifier); return TextButton( child: Text('${player.speed}x'), - onPressed: () { - showModalBottomSheet( + onPressed: () async { + pendingPlayerModals++; + _logger.fine('opening speed selector'); + await showModalBottomSheet( context: context, barrierLabel: 'Select Speed', constraints: const BoxConstraints( @@ -29,6 +35,8 @@ class PlayerSpeedAdjustButton extends HookConsumerWidget { ); }, ); + pendingPlayerModals--; + _logger.fine('Closing speed selector'); }, ); } diff --git a/lib/features/sleep_timer/providers/sleep_timer_provider.dart b/lib/features/sleep_timer/providers/sleep_timer_provider.dart index 910a923..759ea1f 100644 --- a/lib/features/sleep_timer/providers/sleep_timer_provider.dart +++ b/lib/features/sleep_timer/providers/sleep_timer_provider.dart @@ -30,8 +30,7 @@ class SleepTimer extends _$SleepTimer { } var sleepTimer = core.SleepTimer( - // duration: sleepTimerSettings.defaultDuration, - duration: const Duration(seconds: 5), + duration: sleepTimerSettings.defaultDuration, player: ref.watch(simpleAudiobookPlayerProvider), ); ref.onDispose(sleepTimer.dispose); diff --git a/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart b/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart index 08da8ce..ef930bf 100644 --- a/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart +++ b/lib/features/sleep_timer/providers/sleep_timer_provider.g.dart @@ -6,7 +6,7 @@ part of 'sleep_timer_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$sleepTimerHash() => r'de2f39febda3c2234e792f64199c51828206ea9b'; +String _$sleepTimerHash() => r'ad77e82c1b513bbc62815c64ce1ed403f92fc055'; /// See also [SleepTimer]. @ProviderFor(SleepTimer) diff --git a/lib/main.dart b/lib/main.dart index 9454908..1588114 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,8 @@ import 'package:just_audio_media_kit/just_audio_media_kit.dart' import 'package:logging/logging.dart'; import 'package:whispering_pages/api/server_provider.dart'; import 'package:whispering_pages/db/storage.dart'; +import 'package:whispering_pages/features/downloads/core/download_manager.dart'; +import 'package:whispering_pages/features/downloads/providers/download_manager.dart'; import 'package:whispering_pages/features/playback_reporting/providers/playback_reporter_provider.dart'; import 'package:whispering_pages/features/player/providers/audiobook_player.dart'; import 'package:whispering_pages/features/sleep_timer/providers/sleep_timer_provider.dart'; @@ -17,6 +19,7 @@ import 'package:whispering_pages/settings/app_settings_provider.dart'; import 'package:whispering_pages/shared/extensions/duration_format.dart'; import 'package:whispering_pages/theme/theme.dart'; +final appLogger = Logger('whispering_pages'); void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -46,6 +49,9 @@ void main() async { androidNotificationOngoing: true, ); + // for initializing the download manager + await initDownloadManager(); + // run the app runApp( const ProviderScope( @@ -98,6 +104,7 @@ class _EagerInitialization extends ConsumerWidget { ref.watch(simpleAudiobookPlayerProvider); ref.watch(sleepTimerProvider); ref.watch(playbackReporterProvider); + ref.watch(simpleDownloadManagerProvider); } catch (e) { debugPrintStack(stackTrace: StackTrace.current, label: e.toString()); } diff --git a/lib/pages/navigation.dart b/lib/pages/navigation.dart deleted file mode 100644 index 400e3df..0000000 --- a/lib/pages/navigation.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; - -class AppNavigation extends HookConsumerWidget { - const AppNavigation({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final currentIndex = useState(0); - return Scaffold( - bottomNavigationBar: BottomNavigationBar( - currentIndex: currentIndex.value, - onTap: (value) => currentIndex.value = value, - items: const [ - BottomNavigationBarItem( - icon: Icon(Icons.home), - label: 'Home', - ), - BottomNavigationBarItem( - icon: Icon(Icons.business), - label: 'Business', - ), - BottomNavigationBarItem( - icon: Icon(Icons.school), - label: 'School', - ), - ], - ), - ); - } -} diff --git a/lib/router/constants.dart b/lib/router/constants.dart index fe46d5a..fde72b8 100644 --- a/lib/router/constants.dart +++ b/lib/router/constants.dart @@ -43,6 +43,19 @@ class Routes { pathName: 'explore', name: 'explore', ); + + // downloads + static const downloads = _SimpleRoute( + pathName: 'downloads', + name: 'downloads', + ); + + // library browser to browse the library using author, genre, etc. + static const libraryBrowser = _SimpleRoute( + pathName: 'browser', + name: 'libraryBrowser', + // parentRoute: library, + ); } // a class to store path diff --git a/lib/router/router.dart b/lib/router/router.dart index c4d9cca..f06964e 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:whispering_pages/features/downloads/view/downloads_page.dart'; import 'package:whispering_pages/features/explore/view/explore_page.dart'; import 'package:whispering_pages/features/explore/view/search_result_page.dart'; import 'package:whispering_pages/features/item_viewer/view/library_item_page.dart'; +import 'package:whispering_pages/features/library_browser/view/library_browser_page.dart'; import 'package:whispering_pages/features/onboarding/view/onboarding_single_page.dart'; import 'package:whispering_pages/pages/home_page.dart'; import 'package:whispering_pages/settings/view/app_settings_page.dart'; @@ -13,9 +15,9 @@ import 'transitions/slide.dart'; part 'constants.dart'; -final GlobalKey _rootNavigatorKey = +final GlobalKey rootNavigatorKey = GlobalKey(debugLabel: 'root'); -final GlobalKey _sectionHomeNavigatorKey = +final GlobalKey sectionHomeNavigatorKey = GlobalKey(debugLabel: 'HomeNavigator'); // GoRouter configuration @@ -47,7 +49,7 @@ class MyAppRouter { branches: [ // The route branch for the first tab of the bottom navigation bar. StatefulShellBranch( - navigatorKey: _sectionHomeNavigatorKey, + navigatorKey: sectionHomeNavigatorKey, routes: [ GoRoute( path: Routes.home.path, @@ -76,6 +78,23 @@ class MyAppRouter { ); }, ), + // downloads page + GoRoute( + path: Routes.downloads.path, + name: Routes.downloads.name, + pageBuilder: defaultPageBuilder(const DownloadsPage()), + ), + ], + ), + + // Library page + StatefulShellBranch( + routes: [ + GoRoute( + path: Routes.libraryBrowser.path, + name: Routes.libraryBrowser.name, + pageBuilder: defaultPageBuilder(const LibraryBrowserPage()), + ), ], ), // search/explore page diff --git a/lib/router/scaffold_with_nav_bar.dart b/lib/router/scaffold_with_nav_bar.dart index 694360a..d5484be 100644 --- a/lib/router/scaffold_with_nav_bar.dart +++ b/lib/router/scaffold_with_nav_bar.dart @@ -5,6 +5,7 @@ import 'package:miniplayer/miniplayer.dart'; import 'package:whispering_pages/features/explore/providers/search_controller.dart'; import 'package:whispering_pages/features/player/providers/player_form.dart'; import 'package:whispering_pages/features/player/view/audiobook_player.dart'; +import 'package:whispering_pages/features/player/view/player_when_expanded.dart'; // stack to track changes in navigationShell.currentIndex // home is always at index 0 and at the start and should be the last before popping @@ -39,10 +40,11 @@ class ScaffoldWithNavBar extends HookConsumerWidget { final isPlayerExpanded = playerProgress != playerMinHeight; debugPrint( - 'BackButtonListener: Back button pressed, isPlayerExpanded: $isPlayerExpanded, stack: $navigationShellStack', + 'BackButtonListener: Back button pressed, isPlayerExpanded: $isPlayerExpanded, stack: $navigationShellStack, pendingPlayerModals: $pendingPlayerModals', ); + // close miniplayer if it is open - if (isPlayerExpanded) { + if (isPlayerExpanded && pendingPlayerModals == 0) { debugPrint( 'BackButtonListener: closing the player', ); @@ -83,6 +85,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget { return false; } + // TODO: Implement a better way to handle back button presses to minimize player return BackButtonListener( onBackButtonPressed: onBackButtonPressed, child: Scaffold( @@ -161,7 +164,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget { // If it is, debugPrint a message to the console. if (index == navigationShell.currentIndex) { // if current branch is explore, open the search view - if (index == 1) { + if (index == 2) { final searchController = ref.read(globalSearchControllerProvider); // open the search view if not already open if (!searchController.isOpen) { @@ -183,6 +186,12 @@ const _navigationItems = [ icon: Icons.home_outlined, activeIcon: Icons.home, ), + // Library + _NavigationItem( + name: 'Library', + icon: Icons.book_outlined, + activeIcon: Icons.book, + ), _NavigationItem( name: 'Explore', icon: Icons.search_outlined, diff --git a/lib/settings/api_settings_provider.g.dart b/lib/settings/api_settings_provider.g.dart index e2e673c..0bfacbf 100644 --- a/lib/settings/api_settings_provider.g.dart +++ b/lib/settings/api_settings_provider.g.dart @@ -6,7 +6,7 @@ part of 'api_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$apiSettingsHash() => r'b009ae0d14203a15abaa497287fc68f57eb86bde'; +String _$apiSettingsHash() => r'26e7e09e7369bac9fbf0589da9fd97d1f15b7926'; /// See also [ApiSettings]. @ProviderFor(ApiSettings) diff --git a/lib/settings/app_settings_provider.dart b/lib/settings/app_settings_provider.dart index 142019a..18c728b 100644 --- a/lib/settings/app_settings_provider.dart +++ b/lib/settings/app_settings_provider.dart @@ -11,6 +11,20 @@ final _box = AvailableHiveBoxes.userPrefsBox; final _logger = Logger('AppSettingsProvider'); +model.AppSettings readFromBoxOrCreate() { + // see if the settings are already in the box + if (_box.isNotEmpty) { + final foundSettings = _box.getAt(0); + _logger.fine('found settings in box: $foundSettings'); + return foundSettings; + } else { + // create a new settings object + const settings = model.AppSettings(); + _logger.fine('created new settings: $settings'); + return settings; + } +} + @Riverpod(keepAlive: true) class AppSettings extends _$AppSettings { @override @@ -22,20 +36,6 @@ class AppSettings extends _$AppSettings { return state; } - model.AppSettings readFromBoxOrCreate() { - // see if the settings are already in the box - if (_box.isNotEmpty) { - final foundSettings = _box.getAt(0); - _logger.fine('found settings in box: $foundSettings'); - return foundSettings; - } else { - // create a new settings object - const settings = model.AppSettings(); - _logger.fine('created new settings: $settings'); - return settings; - } - } - // write the settings to the box void writeToBox() { _box.clear(); @@ -50,4 +50,8 @@ class AppSettings extends _$AppSettings { void updateState(model.AppSettings newSettings) { state = newSettings; } + + void reset() { + state = const model.AppSettings(); + } } diff --git a/lib/settings/app_settings_provider.g.dart b/lib/settings/app_settings_provider.g.dart index e903f2e..2927d74 100644 --- a/lib/settings/app_settings_provider.g.dart +++ b/lib/settings/app_settings_provider.g.dart @@ -6,7 +6,7 @@ part of 'app_settings_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$appSettingsHash() => r'6716bc568850ffd373fd8572c5781beefafbb9ee'; +String _$appSettingsHash() => r'99bd35aff3c02252a4013c674fd885e841a7f703'; /// See also [AppSettings]. @ProviderFor(AppSettings) diff --git a/lib/settings/models/app_settings.dart b/lib/settings/models/app_settings.dart index b57b9f7..565b35d 100644 --- a/lib/settings/models/app_settings.dart +++ b/lib/settings/models/app_settings.dart @@ -12,8 +12,9 @@ part 'app_settings.g.dart'; class AppSettings with _$AppSettings { const factory AppSettings({ @Default(true) bool isDarkMode, - @Default(false) bool useMaterialThemeOnItemPage, + @Default(true) bool useMaterialThemeOnItemPage, @Default(PlayerSettings()) PlayerSettings playerSettings, + @Default(DownloadSettings()) DownloadSettings downloadSettings, }) = _AppSettings; factory AppSettings.fromJson(Map json) => @@ -105,3 +106,18 @@ class SleepTimerSettings with _$SleepTimerSettings { factory SleepTimerSettings.fromJson(Map json) => _$SleepTimerSettingsFromJson(json); } + +@freezed +class DownloadSettings with _$DownloadSettings { + const factory DownloadSettings({ + @Default(true) bool requiresWiFi, + @Default(3) int retries, + @Default(true) bool allowPause, + @Default(3) int maxConcurrent, + @Default(3) int maxConcurrentByHost, + @Default(3) int maxConcurrentByGroup, + }) = _DownloadSettings; + + factory DownloadSettings.fromJson(Map json) => + _$DownloadSettingsFromJson(json); +} diff --git a/lib/settings/models/app_settings.freezed.dart b/lib/settings/models/app_settings.freezed.dart index e48139b..86c45f9 100644 --- a/lib/settings/models/app_settings.freezed.dart +++ b/lib/settings/models/app_settings.freezed.dart @@ -23,6 +23,7 @@ mixin _$AppSettings { bool get isDarkMode => throw _privateConstructorUsedError; bool get useMaterialThemeOnItemPage => throw _privateConstructorUsedError; PlayerSettings get playerSettings => throw _privateConstructorUsedError; + DownloadSettings get downloadSettings => throw _privateConstructorUsedError; Map toJson() => throw _privateConstructorUsedError; @JsonKey(ignore: true) @@ -39,9 +40,11 @@ abstract class $AppSettingsCopyWith<$Res> { $Res call( {bool isDarkMode, bool useMaterialThemeOnItemPage, - PlayerSettings playerSettings}); + PlayerSettings playerSettings, + DownloadSettings downloadSettings}); $PlayerSettingsCopyWith<$Res> get playerSettings; + $DownloadSettingsCopyWith<$Res> get downloadSettings; } /// @nodoc @@ -60,6 +63,7 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> Object? isDarkMode = null, Object? useMaterialThemeOnItemPage = null, Object? playerSettings = null, + Object? downloadSettings = null, }) { return _then(_value.copyWith( isDarkMode: null == isDarkMode @@ -74,6 +78,10 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> ? _value.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable as PlayerSettings, + downloadSettings: null == downloadSettings + ? _value.downloadSettings + : downloadSettings // ignore: cast_nullable_to_non_nullable + as DownloadSettings, ) as $Val); } @@ -84,6 +92,14 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings> return _then(_value.copyWith(playerSettings: value) as $Val); }); } + + @override + @pragma('vm:prefer-inline') + $DownloadSettingsCopyWith<$Res> get downloadSettings { + return $DownloadSettingsCopyWith<$Res>(_value.downloadSettings, (value) { + return _then(_value.copyWith(downloadSettings: value) as $Val); + }); + } } /// @nodoc @@ -97,10 +113,13 @@ abstract class _$$AppSettingsImplCopyWith<$Res> $Res call( {bool isDarkMode, bool useMaterialThemeOnItemPage, - PlayerSettings playerSettings}); + PlayerSettings playerSettings, + DownloadSettings downloadSettings}); @override $PlayerSettingsCopyWith<$Res> get playerSettings; + @override + $DownloadSettingsCopyWith<$Res> get downloadSettings; } /// @nodoc @@ -117,6 +136,7 @@ class __$$AppSettingsImplCopyWithImpl<$Res> Object? isDarkMode = null, Object? useMaterialThemeOnItemPage = null, Object? playerSettings = null, + Object? downloadSettings = null, }) { return _then(_$AppSettingsImpl( isDarkMode: null == isDarkMode @@ -131,6 +151,10 @@ class __$$AppSettingsImplCopyWithImpl<$Res> ? _value.playerSettings : playerSettings // ignore: cast_nullable_to_non_nullable as PlayerSettings, + downloadSettings: null == downloadSettings + ? _value.downloadSettings + : downloadSettings // ignore: cast_nullable_to_non_nullable + as DownloadSettings, )); } } @@ -140,8 +164,9 @@ class __$$AppSettingsImplCopyWithImpl<$Res> class _$AppSettingsImpl implements _AppSettings { const _$AppSettingsImpl( {this.isDarkMode = true, - this.useMaterialThemeOnItemPage = false, - this.playerSettings = const PlayerSettings()}); + this.useMaterialThemeOnItemPage = true, + this.playerSettings = const PlayerSettings(), + this.downloadSettings = const DownloadSettings()}); factory _$AppSettingsImpl.fromJson(Map json) => _$$AppSettingsImplFromJson(json); @@ -155,10 +180,13 @@ class _$AppSettingsImpl implements _AppSettings { @override @JsonKey() final PlayerSettings playerSettings; + @override + @JsonKey() + final DownloadSettings downloadSettings; @override String toString() { - return 'AppSettings(isDarkMode: $isDarkMode, useMaterialThemeOnItemPage: $useMaterialThemeOnItemPage, playerSettings: $playerSettings)'; + return 'AppSettings(isDarkMode: $isDarkMode, useMaterialThemeOnItemPage: $useMaterialThemeOnItemPage, playerSettings: $playerSettings, downloadSettings: $downloadSettings)'; } @override @@ -173,13 +201,15 @@ class _$AppSettingsImpl implements _AppSettings { other.useMaterialThemeOnItemPage == useMaterialThemeOnItemPage) && (identical(other.playerSettings, playerSettings) || - other.playerSettings == playerSettings)); + other.playerSettings == playerSettings) && + (identical(other.downloadSettings, downloadSettings) || + other.downloadSettings == downloadSettings)); } @JsonKey(ignore: true) @override - int get hashCode => Object.hash( - runtimeType, isDarkMode, useMaterialThemeOnItemPage, playerSettings); + int get hashCode => Object.hash(runtimeType, isDarkMode, + useMaterialThemeOnItemPage, playerSettings, downloadSettings); @JsonKey(ignore: true) @override @@ -199,7 +229,8 @@ abstract class _AppSettings implements AppSettings { const factory _AppSettings( {final bool isDarkMode, final bool useMaterialThemeOnItemPage, - final PlayerSettings playerSettings}) = _$AppSettingsImpl; + final PlayerSettings playerSettings, + final DownloadSettings downloadSettings}) = _$AppSettingsImpl; factory _AppSettings.fromJson(Map json) = _$AppSettingsImpl.fromJson; @@ -211,6 +242,8 @@ abstract class _AppSettings implements AppSettings { @override PlayerSettings get playerSettings; @override + DownloadSettings get downloadSettings; + @override @JsonKey(ignore: true) _$$AppSettingsImplCopyWith<_$AppSettingsImpl> get copyWith => throw _privateConstructorUsedError; @@ -1340,3 +1373,256 @@ abstract class _SleepTimerSettings implements SleepTimerSettings { _$$SleepTimerSettingsImplCopyWith<_$SleepTimerSettingsImpl> get copyWith => throw _privateConstructorUsedError; } + +DownloadSettings _$DownloadSettingsFromJson(Map json) { + return _DownloadSettings.fromJson(json); +} + +/// @nodoc +mixin _$DownloadSettings { + bool get requiresWiFi => throw _privateConstructorUsedError; + int get retries => throw _privateConstructorUsedError; + bool get allowPause => throw _privateConstructorUsedError; + int get maxConcurrent => throw _privateConstructorUsedError; + int get maxConcurrentByHost => throw _privateConstructorUsedError; + int get maxConcurrentByGroup => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $DownloadSettingsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $DownloadSettingsCopyWith<$Res> { + factory $DownloadSettingsCopyWith( + DownloadSettings value, $Res Function(DownloadSettings) then) = + _$DownloadSettingsCopyWithImpl<$Res, DownloadSettings>; + @useResult + $Res call( + {bool requiresWiFi, + int retries, + bool allowPause, + int maxConcurrent, + int maxConcurrentByHost, + int maxConcurrentByGroup}); +} + +/// @nodoc +class _$DownloadSettingsCopyWithImpl<$Res, $Val extends DownloadSettings> + implements $DownloadSettingsCopyWith<$Res> { + _$DownloadSettingsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? requiresWiFi = null, + Object? retries = null, + Object? allowPause = null, + Object? maxConcurrent = null, + Object? maxConcurrentByHost = null, + Object? maxConcurrentByGroup = null, + }) { + return _then(_value.copyWith( + requiresWiFi: null == requiresWiFi + ? _value.requiresWiFi + : requiresWiFi // ignore: cast_nullable_to_non_nullable + as bool, + retries: null == retries + ? _value.retries + : retries // ignore: cast_nullable_to_non_nullable + as int, + allowPause: null == allowPause + ? _value.allowPause + : allowPause // ignore: cast_nullable_to_non_nullable + as bool, + maxConcurrent: null == maxConcurrent + ? _value.maxConcurrent + : maxConcurrent // ignore: cast_nullable_to_non_nullable + as int, + maxConcurrentByHost: null == maxConcurrentByHost + ? _value.maxConcurrentByHost + : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable + as int, + maxConcurrentByGroup: null == maxConcurrentByGroup + ? _value.maxConcurrentByGroup + : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$DownloadSettingsImplCopyWith<$Res> + implements $DownloadSettingsCopyWith<$Res> { + factory _$$DownloadSettingsImplCopyWith(_$DownloadSettingsImpl value, + $Res Function(_$DownloadSettingsImpl) then) = + __$$DownloadSettingsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool requiresWiFi, + int retries, + bool allowPause, + int maxConcurrent, + int maxConcurrentByHost, + int maxConcurrentByGroup}); +} + +/// @nodoc +class __$$DownloadSettingsImplCopyWithImpl<$Res> + extends _$DownloadSettingsCopyWithImpl<$Res, _$DownloadSettingsImpl> + implements _$$DownloadSettingsImplCopyWith<$Res> { + __$$DownloadSettingsImplCopyWithImpl(_$DownloadSettingsImpl _value, + $Res Function(_$DownloadSettingsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? requiresWiFi = null, + Object? retries = null, + Object? allowPause = null, + Object? maxConcurrent = null, + Object? maxConcurrentByHost = null, + Object? maxConcurrentByGroup = null, + }) { + return _then(_$DownloadSettingsImpl( + requiresWiFi: null == requiresWiFi + ? _value.requiresWiFi + : requiresWiFi // ignore: cast_nullable_to_non_nullable + as bool, + retries: null == retries + ? _value.retries + : retries // ignore: cast_nullable_to_non_nullable + as int, + allowPause: null == allowPause + ? _value.allowPause + : allowPause // ignore: cast_nullable_to_non_nullable + as bool, + maxConcurrent: null == maxConcurrent + ? _value.maxConcurrent + : maxConcurrent // ignore: cast_nullable_to_non_nullable + as int, + maxConcurrentByHost: null == maxConcurrentByHost + ? _value.maxConcurrentByHost + : maxConcurrentByHost // ignore: cast_nullable_to_non_nullable + as int, + maxConcurrentByGroup: null == maxConcurrentByGroup + ? _value.maxConcurrentByGroup + : maxConcurrentByGroup // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$DownloadSettingsImpl implements _DownloadSettings { + const _$DownloadSettingsImpl( + {this.requiresWiFi = true, + this.retries = 3, + this.allowPause = true, + this.maxConcurrent = 3, + this.maxConcurrentByHost = 3, + this.maxConcurrentByGroup = 3}); + + factory _$DownloadSettingsImpl.fromJson(Map json) => + _$$DownloadSettingsImplFromJson(json); + + @override + @JsonKey() + final bool requiresWiFi; + @override + @JsonKey() + final int retries; + @override + @JsonKey() + final bool allowPause; + @override + @JsonKey() + final int maxConcurrent; + @override + @JsonKey() + final int maxConcurrentByHost; + @override + @JsonKey() + final int maxConcurrentByGroup; + + @override + String toString() { + return 'DownloadSettings(requiresWiFi: $requiresWiFi, retries: $retries, allowPause: $allowPause, maxConcurrent: $maxConcurrent, maxConcurrentByHost: $maxConcurrentByHost, maxConcurrentByGroup: $maxConcurrentByGroup)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$DownloadSettingsImpl && + (identical(other.requiresWiFi, requiresWiFi) || + other.requiresWiFi == requiresWiFi) && + (identical(other.retries, retries) || other.retries == retries) && + (identical(other.allowPause, allowPause) || + other.allowPause == allowPause) && + (identical(other.maxConcurrent, maxConcurrent) || + other.maxConcurrent == maxConcurrent) && + (identical(other.maxConcurrentByHost, maxConcurrentByHost) || + other.maxConcurrentByHost == maxConcurrentByHost) && + (identical(other.maxConcurrentByGroup, maxConcurrentByGroup) || + other.maxConcurrentByGroup == maxConcurrentByGroup)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, requiresWiFi, retries, + allowPause, maxConcurrent, maxConcurrentByHost, maxConcurrentByGroup); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$DownloadSettingsImplCopyWith<_$DownloadSettingsImpl> get copyWith => + __$$DownloadSettingsImplCopyWithImpl<_$DownloadSettingsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$DownloadSettingsImplToJson( + this, + ); + } +} + +abstract class _DownloadSettings implements DownloadSettings { + const factory _DownloadSettings( + {final bool requiresWiFi, + final int retries, + final bool allowPause, + final int maxConcurrent, + final int maxConcurrentByHost, + final int maxConcurrentByGroup}) = _$DownloadSettingsImpl; + + factory _DownloadSettings.fromJson(Map json) = + _$DownloadSettingsImpl.fromJson; + + @override + bool get requiresWiFi; + @override + int get retries; + @override + bool get allowPause; + @override + int get maxConcurrent; + @override + int get maxConcurrentByHost; + @override + int get maxConcurrentByGroup; + @override + @JsonKey(ignore: true) + _$$DownloadSettingsImplCopyWith<_$DownloadSettingsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/settings/models/app_settings.g.dart b/lib/settings/models/app_settings.g.dart index 434f306..6e4c0e7 100644 --- a/lib/settings/models/app_settings.g.dart +++ b/lib/settings/models/app_settings.g.dart @@ -10,11 +10,15 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map json) => _$AppSettingsImpl( isDarkMode: json['isDarkMode'] as bool? ?? true, useMaterialThemeOnItemPage: - json['useMaterialThemeOnItemPage'] as bool? ?? false, + json['useMaterialThemeOnItemPage'] as bool? ?? true, playerSettings: json['playerSettings'] == null ? const PlayerSettings() : PlayerSettings.fromJson( json['playerSettings'] as Map), + downloadSettings: json['downloadSettings'] == null + ? const DownloadSettings() + : DownloadSettings.fromJson( + json['downloadSettings'] as Map), ); Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => @@ -22,6 +26,7 @@ Map _$$AppSettingsImplToJson(_$AppSettingsImpl instance) => 'isDarkMode': instance.isDarkMode, 'useMaterialThemeOnItemPage': instance.useMaterialThemeOnItemPage, 'playerSettings': instance.playerSettings, + 'downloadSettings': instance.downloadSettings, }; _$PlayerSettingsImpl _$$PlayerSettingsImplFromJson(Map json) => @@ -155,3 +160,26 @@ const _$SleepTimerShakeSenseModeEnumMap = { SleepTimerShakeSenseMode.always: 'always', SleepTimerShakeSenseMode.nearEnds: 'nearEnds', }; + +_$DownloadSettingsImpl _$$DownloadSettingsImplFromJson( + Map json) => + _$DownloadSettingsImpl( + requiresWiFi: json['requiresWiFi'] as bool? ?? true, + retries: (json['retries'] as num?)?.toInt() ?? 3, + allowPause: json['allowPause'] as bool? ?? true, + maxConcurrent: (json['maxConcurrent'] as num?)?.toInt() ?? 3, + maxConcurrentByHost: (json['maxConcurrentByHost'] as num?)?.toInt() ?? 3, + maxConcurrentByGroup: + (json['maxConcurrentByGroup'] as num?)?.toInt() ?? 3, + ); + +Map _$$DownloadSettingsImplToJson( + _$DownloadSettingsImpl instance) => + { + 'requiresWiFi': instance.requiresWiFi, + 'retries': instance.retries, + 'allowPause': instance.allowPause, + 'maxConcurrent': instance.maxConcurrent, + 'maxConcurrentByHost': instance.maxConcurrentByHost, + 'maxConcurrentByGroup': instance.maxConcurrentByGroup, + }; diff --git a/lib/settings/view/app_settings_page.dart b/lib/settings/view/app_settings_page.dart index 2c9f342..591a987 100644 --- a/lib/settings/view/app_settings_page.dart +++ b/lib/settings/view/app_settings_page.dart @@ -1,4 +1,7 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_settings_ui/flutter_settings_ui.dart'; import 'package:go_router/go_router.dart'; @@ -7,6 +10,7 @@ import 'package:whispering_pages/api/authenticated_user_provider.dart'; import 'package:whispering_pages/api/server_provider.dart'; import 'package:whispering_pages/router/router.dart'; import 'package:whispering_pages/settings/app_settings_provider.dart'; +import 'package:whispering_pages/settings/models/app_settings.dart' as model; class AppSettingsPage extends HookConsumerWidget { const AppSettingsPage({ @@ -20,7 +24,6 @@ class AppSettingsPage extends HookConsumerWidget { final registeredServersAsList = registeredServers.toList(); final availableUsers = ref.watch(authenticatedUserProvider); final serverURIController = useTextEditingController(); - final formKey = GlobalKey(); final sleepTimerSettings = appSettings.playerSettings.sleepTimerSettings; return Scaffold( @@ -124,6 +127,149 @@ class AppSettingsPage extends HookConsumerWidget { ), ], ), + + // Backup and Restore section + SettingsSection( + margin: const EdgeInsetsDirectional.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + title: Text( + 'Backup and Restore', + style: Theme.of(context).textTheme.titleLarge, + ), + tiles: [ + SettingsTile( + title: const Text('Copy to Clipboard'), + leading: const Icon(Icons.copy), + description: const Text( + 'Copy the app settings to the clipboard', + ), + onPressed: (context) async { + // copy to clipboard + await Clipboard.setData( + ClipboardData( + text: jsonEncode(appSettings.toJson()), + ), + ); + // show toast + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Settings copied to clipboard'), + ), + ); + }, + ), + SettingsTile( + title: const Text('Restore'), + leading: const Icon(Icons.restore), + description: const Text( + 'Restore the app settings from the backup', + ), + onPressed: (context) { + final formKey = GlobalKey(); + // show a dialog to get the backup + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Restore Backup'), + content: Form( + key: formKey, + child: TextFormField( + controller: serverURIController, + decoration: const InputDecoration( + labelText: 'Backup', + hintText: 'Paste the backup here', + ), + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please paste the backup here'; + } + return null; + }, + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + if (formKey.currentState!.validate()) { + final backup = serverURIController.text; + final newSettings = model.AppSettings.fromJson( + // decode the backup as json + jsonDecode(backup), + ); + ref + .read(appSettingsProvider.notifier) + .updateState(newSettings); + Navigator.of(context).pop(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Settings restored'), + ), + ); + // clear the backup + serverURIController.clear(); + } + }, + child: const Text('Restore'), + ), + ], + ); + }, + ); + }, + ), + + // a button to reset the app settings + SettingsTile( + title: const Text('Reset App Settings'), + leading: const Icon(Icons.settings_backup_restore), + description: const Text( + 'Reset the app settings to the default values', + ), + onPressed: (context) async { + // confirm the reset + final res = await showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Reset App Settings'), + content: const Text( + 'Are you sure you want to reset the app settings?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(true); + }, + child: const Text('Reset'), + ), + ], + ); + }, + ); + + // if the user confirms the reset + if (res == true) { + ref.read(appSettingsProvider.notifier).reset(); + } + }, + ), + ], + ), ], ), ); diff --git a/lib/shared/extensions/model_conversions.dart b/lib/shared/extensions/model_conversions.dart index def0639..38403d8 100644 --- a/lib/shared/extensions/model_conversions.dart +++ b/lib/shared/extensions/model_conversions.dart @@ -51,3 +51,31 @@ extension UserConversion on User { UserWithSessionAndMostRecentProgress.fromJson(toJson()); User get asUser => User.fromJson(toJson()); } + +extension ContentUrl on LibraryFile { + Uri url(String baseUrl, String itemId, String token) { + // /api/items/{itemId}/file/{ino}?{token} + // return Uri.parse('$baseUrl/api/items/$itemId/file/$ino?token=$token'); + var baseUri = Uri.parse(baseUrl); + return Uri( + scheme: baseUri.scheme, + host: baseUri.host, + path: '/api/items/$itemId/file/$ino', + queryParameters: {'token': token}, + ); + } + + Uri downloadUrl(String baseUrl, String itemId, String token) { + // /api/items/{itemId}/file/{ino}/download?{token} + // return Uri.parse( + // '$baseUrl/api/items/$itemId/file/$ino/download?token=$token', + // ); + var baseUri = Uri.parse(baseUrl); + return Uri( + scheme: baseUri.scheme, + host: baseUri.host, + path: '/api/items/$itemId/file/$ino/download', + queryParameters: {'token': token}, + ); + } +} diff --git a/lib/theme/theme_from_cover_provider.g.dart b/lib/theme/theme_from_cover_provider.g.dart index a2c3594..8201917 100644 --- a/lib/theme/theme_from_cover_provider.g.dart +++ b/lib/theme/theme_from_cover_provider.g.dart @@ -6,7 +6,7 @@ part of 'theme_from_cover_provider.dart'; // RiverpodGenerator // ************************************************************************** -String _$themeFromCoverHash() => r'bb4c5f32dfe7b6da6f43b8d002267d554cdf98ec'; +String _$themeFromCoverHash() => r'a549513a0dcdff76be94488baf38a8b886ce63eb'; /// Copied from Dart SDK class _SystemHash { diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index c04e20caf6370ebb9253ad831cc31de4a9c965f6..db02f863d953b259ece42141a6698c5dc7916c66 100644 GIT binary patch literal 199810 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

$y+2?ieR;|2{9b=|`)VO+ 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