From 307a8e21df1e39123a1dca4c1b063eab50359581 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Thu, 25 Aug 2022 09:53:49 +0600 Subject: [PATCH] fix: dropped flutter_downloader deps due to slow download speed and UserDownloads not showing for anonymous --- lib/components/Library/UserDownloads.dart | 60 ++++---- lib/components/Library/UserLibrary.dart | 17 +-- lib/components/Shared/AnonymousFallback.dart | 15 +- lib/main.dart | 12 -- lib/provider/Downloader.dart | 145 ++++++++----------- pubspec.lock | 7 - pubspec.yaml | 1 - 7 files changed, 112 insertions(+), 145 deletions(-) diff --git a/lib/components/Library/UserDownloads.dart b/lib/components/Library/UserDownloads.dart index ab7f5623..ac7d3fd2 100644 --- a/lib/components/Library/UserDownloads.dart +++ b/lib/components/Library/UserDownloads.dart @@ -13,7 +13,6 @@ class UserDownloads extends HookConsumerWidget { Widget build(BuildContext context, ref) { final downloader = ref.watch(downloaderProvider); - final inQueue = downloader.inQueue.toList(); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -43,39 +42,40 @@ class UserDownloads extends HookConsumerWidget { ], ), ), - ListView.builder( - itemCount: inQueue.length, - shrinkWrap: true, - itemBuilder: (context, index) { - final track = inQueue[index]; - return ListTile( - title: Text(track.name!), - leading: Padding( - padding: const EdgeInsets.symmetric(horizontal: 5), - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: CachedNetworkImage( - height: 40, - width: 40, - imageUrl: TypeConversionUtils.image_X_UrlString( - track.album?.images, + Expanded( + child: ListView.builder( + itemCount: downloader.inQueue.length, + 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: CachedNetworkImage( + height: 40, + width: 40, + imageUrl: TypeConversionUtils.image_X_UrlString( + track.album?.images, + ), ), ), ), - ), - trailing: const SizedBox( - width: 30, - height: 30, - child: CircularProgressIndicator.adaptive(), - ), - horizontalTitleGap: 5, - subtitle: Text( - TypeConversionUtils.artists_X_String( - track.artists ?? [], + trailing: const SizedBox( + width: 30, + height: 30, + child: CircularProgressIndicator.adaptive(), ), - ), - ); - }, + horizontalTitleGap: 5, + subtitle: Text( + TypeConversionUtils.artists_X_String( + track.artists ?? [], + ), + ), + ); + }, + ), ), ], ); diff --git a/lib/components/Library/UserLibrary.dart b/lib/components/Library/UserLibrary.dart index 84670c63..098298f9 100644 --- a/lib/components/Library/UserLibrary.dart +++ b/lib/components/Library/UserLibrary.dart @@ -5,14 +5,11 @@ import 'package:spotube/components/Library/UserArtists.dart'; import 'package:spotube/components/Library/UserDownloads.dart'; import 'package:spotube/components/Library/UserPlaylists.dart'; import 'package:spotube/components/Shared/AnonymousFallback.dart'; -import 'package:spotube/provider/Auth.dart'; class UserLibrary extends ConsumerWidget { const UserLibrary({Key? key}) : super(key: key); @override Widget build(BuildContext context, ref) { - final Auth auth = ref.watch(authProvider); - return Expanded( child: DefaultTabController( length: 4, @@ -27,14 +24,12 @@ class UserLibrary extends ConsumerWidget { Tab(text: "Downloads"), ], ), - body: auth.isLoggedIn - ? TabBarView(children: [ - const UserPlaylists(), - UserArtists(), - const UserAlbums(), - const UserDownloads(), - ]) - : const AnonymousFallback(), + body: TabBarView(children: [ + const AnonymousFallback(child: UserPlaylists()), + AnonymousFallback(child: UserArtists()), + const AnonymousFallback(child: UserAlbums()), + const UserDownloads(), + ]), ), ), ), diff --git a/lib/components/Shared/AnonymousFallback.dart b/lib/components/Shared/AnonymousFallback.dart index b7e949f2..d5fe727c 100644 --- a/lib/components/Shared/AnonymousFallback.dart +++ b/lib/components/Shared/AnonymousFallback.dart @@ -1,11 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:spotube/provider/Auth.dart'; -class AnonymousFallback extends StatelessWidget { - const AnonymousFallback({Key? key}) : super(key: key); +class AnonymousFallback extends ConsumerWidget { + final Widget? child; + const AnonymousFallback({ + Key? key, + this.child, + }) : super(key: key); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { + final isLoggedIn = ref.watch(authProvider.select((s) => s.isLoggedIn)); + + if (isLoggedIn && child != null) return child!; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/main.dart b/lib/main.dart index fa5a8777..b3b45dfc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,9 +2,7 @@ import 'dart:convert'; import 'package:audio_service/audio_service.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hive_flutter/hive_flutter.dart'; @@ -48,11 +46,6 @@ void main() async { } appWindow.show(); }); - } else if (kIsMobile) { - await FlutterDownloader.initialize( - debug: kDebugMode, - ignoreSsl: true, - ); } MobileAudioService? audioServiceHandler; runApp( @@ -155,13 +148,8 @@ class _SpotubeState extends ConsumerState with WidgetsBindingObserver { super.initState(); SharedPreferences.getInstance().then(((value) => localStorage = value)); WidgetsBinding.instance.addObserver(this); - if (kIsMobile) FlutterDownloader.registerCallback(downloadCallback); } - @pragma('vm:entry-point') - static void downloadCallback( - String id, DownloadTaskStatus status, int progress) {} - @override void dispose() { WidgetsBinding.instance.removeObserver(this); diff --git a/lib/provider/Downloader.dart b/lib/provider/Downloader.dart index 2c9bf522..75c5f4cc 100644 --- a/lib/provider/Downloader.dart +++ b/lib/provider/Downloader.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/widgets.dart'; -import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:queue/queue.dart'; import 'package:path/path.dart' as path; @@ -12,7 +11,6 @@ import 'package:spotube/models/SpotubeTrack.dart'; import 'package:spotube/provider/Playback.dart'; import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/provider/YouTube.dart'; -import 'package:spotube/utils/platform.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart'; Queue queueInstance = Queue(delay: const Duration(seconds: 5)); @@ -38,87 +36,76 @@ class Downloader with ChangeNotifier { final logger = getLogger(Downloader); + Playback get _playback => ref.read(playbackProvider); + void addToQueue(Track baseTrack) async { if (inQueue.any((t) => t.id == baseTrack.id!)) return; inQueue.add(baseTrack); currentlyRunning++; notifyListeners(); - if (kIsMobile) { - grabberQueue.add(() async { - final track = await ref.read(playbackProvider).toSpotubeTrack( - baseTrack, - noSponsorBlock: true, - ); - final filename = '${track.ytTrack.title}.mp3'; - - final url = - ((await yt.videos.streamsClient.getManifest(track.ytTrack.url))) - .audioOnly - .where((audio) => audio.codec.mimeType == "audio/mp4") - .withHighestBitrate() - .url; - await FlutterDownloader.enqueue( - savedDir: downloadPath, - url: url.toString(), - fileName: filename, - openFileFromNotification: true, - showNotification: true, + // Using android Audio Focus to keep the app run in background + _playback.mobileAudioService?.session?.setActive(true); + grabberQueue.add(() async { + final track = await ref.read(playbackProvider).toSpotubeTrack( + baseTrack, + noSponsorBlock: true, + ); + _queue.add(() async { + final cleanTitle = track.ytTrack.title.replaceAll( + RegExp(r'[/\\?%*:|"<>]'), + "", ); - }); - } else { - grabberQueue.add(() async { - final track = await ref.read(playbackProvider).toSpotubeTrack( - baseTrack, - noSponsorBlock: true, - ); - _queue.add(() async { - final filename = '${track.ytTrack.title}.mp3'; - final file = File(path.join(downloadPath, filename)); - try { - logger.v("[addToQueue] Download starting for ${file.path}"); - if (file.existsSync() && await onFileExists?.call(track) != true) { - return; - } - file.createSync(recursive: true); - StreamManifest manifest = - await yt.videos.streamsClient.getManifest(track.ytTrack.url); - logger.v( - "[addToQueue] Getting download information for ${file.path}", - ); - final audioStream = yt.videos.streamsClient - .get( - manifest.audioOnly - .where((audio) => audio.codec.mimeType == "audio/mp4") - .withHighestBitrate(), - ) - .asBroadcastStream(); - - logger.v( - "[addToQueue] ${file.path} download started", - ); - - IOSink outputFileStream = file.openWrite(); - await audioStream.pipe(outputFileStream); - await outputFileStream.flush(); - logger.v( - "[addToQueue] Download of ${file.path} is done successfully", - ); - } catch (e, stack) { - logger.e( - "[addToQueue] Failed download of ${file.path}", - e, - stack, - ); - rethrow; - } finally { - currentlyRunning--; - inQueue.removeWhere((t) => t.id == track.id); - notifyListeners(); + final filename = '$cleanTitle.mp3'; + final file = File(path.join(downloadPath, filename)); + try { + logger.v("[addToQueue] Download starting for ${file.path}"); + if (file.existsSync() && await onFileExists?.call(track) != true) { + return; } - }); + file.createSync(recursive: true); + StreamManifest manifest = + await yt.videos.streamsClient.getManifest(track.ytTrack.url); + logger.v( + "[addToQueue] Getting download information for ${file.path}", + ); + final audioStream = yt.videos.streamsClient + .get( + manifest.audioOnly + .where( + (audio) => audio.codec.mimeType == "audio/mp4", + ) + .withHighestBitrate(), + ) + .asBroadcastStream(); + + logger.v( + "[addToQueue] ${file.path} download started", + ); + + IOSink outputFileStream = file.openWrite(); + await audioStream.pipe(outputFileStream); + await outputFileStream.flush(); + logger.v( + "[addToQueue] Download of ${file.path} is done successfully", + ); + } catch (e, stack) { + logger.e( + "[addToQueue] Failed download of ${file.path}", + e, + stack, + ); + rethrow; + } finally { + currentlyRunning--; + inQueue.removeWhere((t) => t.id == track.id); + if (currentlyRunning == 0 && !_playback.isPlaying) { + _playback.mobileAudioService?.session?.setActive(false); + } + notifyListeners(); + } }); - } + }); } cancelAll() { @@ -126,13 +113,9 @@ class Downloader with ChangeNotifier { grabberQueue = Queue(); inQueue.clear(); currentlyRunning = 0; - if (kIsMobile) { - FlutterDownloader.cancelAll(); - } else { - _queue.cancel(); - queueInstance = Queue(); - _queue = queueInstance; - } + _queue.cancel(); + queueInstance = Queue(); + _queue = queueInstance; notifyListeners(); } } diff --git a/pubspec.lock b/pubspec.lock index 5861dd44..bc06f3ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -552,13 +552,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.0.9" - flutter_downloader: - dependency: "direct main" - description: - name: flutter_downloader - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" flutter_hooks: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7a341e82..abdfc023 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,7 +68,6 @@ dependencies: file_picker: ^4.6.1 popover: ^0.2.6+3 queue: ^3.1.0+1 - flutter_downloader: ^1.8.1 auto_size_text: ^3.0.0 badges: ^2.0.3