Skip to content

Commit

Permalink
feat: Better download manager with download progress
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Jun 8, 2023
1 parent f4b0d13 commit 6752adc
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 249 deletions.
91 changes: 61 additions & 30 deletions lib/components/library/user_downloads.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:background_downloader/background_downloader.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';

import 'package:spotify/spotify.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/image/universal_image.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

class UserDownloads extends HookConsumerWidget {
const UserDownloads({Key? key}) : super(key: key);

@override
Widget build(BuildContext context, ref) {
final downloader = ref.watch(downloaderProvider);
ref.watch(downloadManagerProvider);
final downloadManager = ref.watch(downloadManagerProvider.notifier);

return Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expand All @@ -26,7 +30,7 @@ class UserDownloads extends HookConsumerWidget {
Expanded(
child: AutoSizeText(
context.l10n
.currently_downloading(downloader.currentlyRunning),
.currently_downloading(downloadManager.totalDownloads),
maxLines: 1,
style: Theme.of(context).textTheme.headlineMedium,
),
Expand All @@ -37,9 +41,9 @@ class UserDownloads extends HookConsumerWidget {
backgroundColor: Colors.red[50],
foregroundColor: Colors.red[400],
),
onPressed: downloader.currentlyRunning > 0
? downloader.cancelAll
: null,
onPressed: downloadManager.totalDownloads == 0
? null
: downloadManager.cancelAll,
child: Text(context.l10n.cancel_all),
),
],
Expand All @@ -48,36 +52,63 @@ class UserDownloads extends HookConsumerWidget {
Expanded(
child: SafeArea(
child: ListView.builder(
itemCount: downloader.inQueue.length,
itemCount: downloadManager.totalDownloads,
itemBuilder: (context, index) {
final track = downloader.inQueue.elementAt(index);
return ListTile(
title: Text(track.name ?? ''),
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: UniversalImage(
height: 40,
width: 40,
path: TypeConversionUtils.image_X_UrlString(
track.album?.images,
placeholder: ImagePlaceholder.albumArt,
final track = downloadManager.items.elementAt(index);
return HookBuilder(builder: (context) {
final task = useStream(
downloadManager.activeDownloadProgress.stream
.where((element) => element.task.taskId == track.id),
);
final failedTaskStream = useStream(
downloadManager.failedDownloads.stream
.where((element) => element.taskId == track.id),
);
final taskItSelf = useFuture(
FileDownloader().database.recordForId(track.id!),
);

final hasFailed = failedTaskStream.hasData ||
taskItSelf.data?.status == TaskStatus.failed;

return ListTile(
title: Text(track.name ?? ''),
leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: UniversalImage(
height: 40,
width: 40,
path: TypeConversionUtils.image_X_UrlString(
track.album?.images,
placeholder: ImagePlaceholder.albumArt,
),
),
),
),
),
trailing: const SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator(),
),
subtitle: Text(
TypeConversionUtils.artists_X_String(
horizontalTitleGap: 10,
trailing: SizedBox(
width: 30,
height: 30,
child: downloadManager.activeItem?.id == track.id
? CircularProgressIndicator(
value: task.data?.progress ?? 0,
)
: hasFailed
? Icon(SpotubeIcons.error, color: Colors.red[400])
: IconButton(
icon: const Icon(SpotubeIcons.close),
onPressed: () {
downloadManager.cancel(track);
}),
),
subtitle: TypeConversionUtils.artists_X_ClickableArtists(
track.artists ?? <Artist>[],
mainAxisAlignment: WrapAlignment.start,
),
),
);
);
});
},
),
),
Expand Down
11 changes: 6 additions & 5 deletions lib/components/player/player_actions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import 'package:spotube/components/shared/heart_button.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/local_track.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/utils/type_conversion_utils.dart';

Expand All @@ -32,9 +32,10 @@ class PlayerActions extends HookConsumerWidget {
Widget build(BuildContext context, ref) {
final playlist = ref.watch(ProxyPlaylistNotifier.provider);
final isLocalTrack = playlist.activeTrack is LocalTrack;
final downloader = ref.watch(downloaderProvider);
final isInQueue = downloader.inQueue
.any((element) => element.id == playlist.activeTrack?.id);
ref.watch(downloadManagerProvider);
final downloader = ref.watch(downloadManagerProvider.notifier);
final isInQueue = downloader.activeItem != null &&
downloader.activeItem!.id == playlist.activeTrack?.id;
final localTracks = [] /* ref.watch(localTracksProvider).value */;
final auth = ref.watch(AuthenticationNotifier.provider);

Expand Down Expand Up @@ -121,7 +122,7 @@ class PlayerActions extends HookConsumerWidget {
isDownloaded ? SpotubeIcons.done : SpotubeIcons.download,
),
onPressed: playlist.activeTrack != null
? () => downloader.addToQueue(playlist.activeTrack!)
? () => downloader.enqueue(playlist.activeTrack!)
: null,
),
if (playlist.activeTrack != null && !isLocalTrack && auth != null)
Expand Down
5 changes: 2 additions & 3 deletions lib/components/root/sidebar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:flutter/material.dart';
import 'package:motion_toast/motion_toast.dart';
import 'package:sidebarx/sidebarx.dart';

import 'package:spotube/collections/assets.gen.dart';
Expand All @@ -14,8 +13,8 @@ import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_brightness_value.dart';
import 'package:spotube/hooks/use_sidebarx_controller.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';

import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/queries/queries.dart';
Expand Down Expand Up @@ -53,7 +52,7 @@ class Sidebar extends HookConsumerWidget {
final breakpoints = useBreakpoints();

final downloadCount = ref.watch(
downloaderProvider.select((s) => s.currentlyRunning),
downloadManagerProvider.select((s) => s.length),
);

final layoutMode =
Expand Down
4 changes: 2 additions & 2 deletions lib/components/root/spotube_navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/hooks/use_brightness_value.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';

class SpotubeNavigationBar extends HookConsumerWidget {
Expand All @@ -27,7 +27,7 @@ class SpotubeNavigationBar extends HookConsumerWidget {
Widget build(BuildContext context, ref) {
final theme = Theme.of(context);
final downloadCount = ref.watch(
downloaderProvider.select((s) => s.currentlyRunning),
downloadManagerProvider.select((s) => s.length),
);
final breakpoint = useBreakpoints();
final layoutMode =
Expand Down
13 changes: 7 additions & 6 deletions lib/components/shared/track_table/tracks_table_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import 'package:spotube/components/shared/track_table/track_tile.dart';
import 'package:spotube/components/library/user_local_tracks.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/proxy_playlist/proxy_playlist_provider.dart';
import 'package:spotube/utils/primitive_utils.dart';
import 'package:spotube/utils/service_utils.dart';
Expand Down Expand Up @@ -43,7 +43,8 @@ class TracksTableView extends HookConsumerWidget {
Widget build(context, ref) {
final playlist = ref.watch(ProxyPlaylistNotifier.provider);
final playback = ref.watch(ProxyPlaylistNotifier.notifier);
final downloader = ref.watch(downloaderProvider);
ref.watch(downloadManagerProvider);
final downloader = ref.watch(downloadManagerProvider.notifier);
TextStyle tableHeadStyle =
const TextStyle(fontWeight: FontWeight.bold, fontSize: 16);

Expand Down Expand Up @@ -208,11 +209,11 @@ class TracksTableView extends HookConsumerWidget {
},
);
if (confirmed != true) return;
for (final selectedTrack in selectedTracks) {
downloader.addToQueue(selectedTrack);
await downloader.enqueueAll(selectedTracks.toList());
if (context.mounted) {
selected.value = [];
showCheck.value = false;
}
selected.value = [];
showCheck.value = false;
break;
}
case "add-to-playlist":
Expand Down
33 changes: 0 additions & 33 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ import 'package:metadata_god/metadata_god.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart';
import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/intents.dart';
import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/models/logger.dart';
import 'package:spotube/models/matched_track.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/palette_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
Expand Down Expand Up @@ -143,37 +141,6 @@ Future<void> main(List<String> rawArgs) async {
enabled: !kReleaseMode,
builder: (context) {
return ProviderScope(
overrides: [
downloaderProvider.overrideWith(
(ref) {
return Downloader(
ref,
queueInstance,
downloadPath: ref.watch(
userPreferencesProvider.select(
(s) => s.downloadLocation,
),
),
onFileExists: (track) {
final logger = getLogger(Downloader);
try {
logger.v(
"[onFileExists] download confirmation for ${track.name}",
);
return showDialog<bool>(
context: context,
builder: (_) =>
ReplaceDownloadedDialog(track: track),
).then((s) => s ?? false);
} catch (e, stack) {
Catcher.reportCheckedError(e, stack);
return false;
}
},
);
},
)
],
child: QueryClientProvider(
staleDuration: const Duration(minutes: 30),
child: const Spotube(),
Expand Down
4 changes: 2 additions & 2 deletions lib/pages/root/root_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import 'package:spotube/components/root/bottom_player.dart';
import 'package:spotube/components/root/sidebar.dart';
import 'package:spotube/components/root/spotube_navigation_bar.dart';
import 'package:spotube/hooks/use_update_checker.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';

const rootPaths = {
0: "/",
Expand All @@ -31,7 +31,7 @@ class RootApp extends HookConsumerWidget {
final isMounted = useIsMounted();
final auth = ref.watch(AuthenticationNotifier.provider);

final downloader = ref.watch(downloaderProvider);
final downloader = ref.watch(downloadManagerProvider.notifier);
useEffect(() {
downloader.onFileExists = (track) async {
if (!isMounted()) return false;
Expand Down
5 changes: 2 additions & 3 deletions lib/pages/settings/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'package:flutter_desktop_tools/flutter_desktop_tools.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:piped_client/piped_client.dart';
import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/language_codes.dart';

Expand All @@ -20,8 +19,8 @@ import 'package:spotube/collections/spotify_markets.dart';
import 'package:spotube/extensions/context.dart';
import 'package:spotube/hooks/use_breakpoints.dart';
import 'package:spotube/l10n/l10n.dart';
import 'package:spotube/provider/download_manager_provider.dart';
import 'package:spotube/provider/authentication_provider.dart';
import 'package:spotube/provider/downloader_provider.dart';
import 'package:spotube/provider/user_preferences_provider.dart';
import 'package:spotube/provider/piped_provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
Expand All @@ -34,7 +33,7 @@ class SettingsPage extends HookConsumerWidget {
final UserPreferences preferences = ref.watch(userPreferencesProvider);
final auth = ref.watch(AuthenticationNotifier.provider);
final isDownloading =
ref.watch(downloaderProvider.select((s) => s.currentlyRunning > 0));
ref.watch(downloadManagerProvider.select((s) => s.isNotEmpty));
final theme = Theme.of(context);

final pickColorScheme = useCallback(() {
Expand Down
Loading

0 comments on commit 6752adc

Please sign in to comment.