fix: dropped flutter_downloader deps due to slow download speed and UserDownloads not showing for anonymous

This commit is contained in:
Kingkor Roy Tirtho 2022-08-25 09:53:49 +06:00
parent aba1ba9325
commit 307a8e21df
7 changed files with 112 additions and 145 deletions

View File

@ -13,7 +13,6 @@ class UserDownloads extends HookConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final downloader = ref.watch(downloaderProvider); final downloader = ref.watch(downloaderProvider);
final inQueue = downloader.inQueue.toList();
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -43,39 +42,40 @@ class UserDownloads extends HookConsumerWidget {
], ],
), ),
), ),
ListView.builder( Expanded(
itemCount: inQueue.length, child: ListView.builder(
shrinkWrap: true, itemCount: downloader.inQueue.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final track = inQueue[index]; final track = downloader.inQueue.elementAt(index);
return ListTile( return ListTile(
title: Text(track.name!), title: Text(track.name!),
leading: Padding( leading: Padding(
padding: const EdgeInsets.symmetric(horizontal: 5), padding: const EdgeInsets.symmetric(horizontal: 5),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
child: CachedNetworkImage( child: CachedNetworkImage(
height: 40, height: 40,
width: 40, width: 40,
imageUrl: TypeConversionUtils.image_X_UrlString( imageUrl: TypeConversionUtils.image_X_UrlString(
track.album?.images, track.album?.images,
),
), ),
), ),
), ),
), trailing: const SizedBox(
trailing: const SizedBox( width: 30,
width: 30, height: 30,
height: 30, child: CircularProgressIndicator.adaptive(),
child: CircularProgressIndicator.adaptive(),
),
horizontalTitleGap: 5,
subtitle: Text(
TypeConversionUtils.artists_X_String<Artist>(
track.artists ?? [],
), ),
), horizontalTitleGap: 5,
); subtitle: Text(
}, TypeConversionUtils.artists_X_String<Artist>(
track.artists ?? [],
),
),
);
},
),
), ),
], ],
); );

View File

@ -5,14 +5,11 @@ import 'package:spotube/components/Library/UserArtists.dart';
import 'package:spotube/components/Library/UserDownloads.dart'; import 'package:spotube/components/Library/UserDownloads.dart';
import 'package:spotube/components/Library/UserPlaylists.dart'; import 'package:spotube/components/Library/UserPlaylists.dart';
import 'package:spotube/components/Shared/AnonymousFallback.dart'; import 'package:spotube/components/Shared/AnonymousFallback.dart';
import 'package:spotube/provider/Auth.dart';
class UserLibrary extends ConsumerWidget { class UserLibrary extends ConsumerWidget {
const UserLibrary({Key? key}) : super(key: key); const UserLibrary({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final Auth auth = ref.watch(authProvider);
return Expanded( return Expanded(
child: DefaultTabController( child: DefaultTabController(
length: 4, length: 4,
@ -27,14 +24,12 @@ class UserLibrary extends ConsumerWidget {
Tab(text: "Downloads"), Tab(text: "Downloads"),
], ],
), ),
body: auth.isLoggedIn body: TabBarView(children: [
? TabBarView(children: [ const AnonymousFallback(child: UserPlaylists()),
const UserPlaylists(), AnonymousFallback(child: UserArtists()),
UserArtists(), const AnonymousFallback(child: UserAlbums()),
const UserAlbums(), const UserDownloads(),
const UserDownloads(), ]),
])
: const AnonymousFallback(),
), ),
), ),
), ),

View File

@ -1,11 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:spotube/provider/Auth.dart';
class AnonymousFallback extends StatelessWidget { class AnonymousFallback extends ConsumerWidget {
const AnonymousFallback({Key? key}) : super(key: key); final Widget? child;
const AnonymousFallback({
Key? key,
this.child,
}) : super(key: key);
@override @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( return Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,

View File

@ -2,9 +2,7 @@ import 'dart:convert';
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
@ -48,11 +46,6 @@ void main() async {
} }
appWindow.show(); appWindow.show();
}); });
} else if (kIsMobile) {
await FlutterDownloader.initialize(
debug: kDebugMode,
ignoreSsl: true,
);
} }
MobileAudioService? audioServiceHandler; MobileAudioService? audioServiceHandler;
runApp( runApp(
@ -155,13 +148,8 @@ class _SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
super.initState(); super.initState();
SharedPreferences.getInstance().then(((value) => localStorage = value)); SharedPreferences.getInstance().then(((value) => localStorage = value));
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
if (kIsMobile) FlutterDownloader.registerCallback(downloadCallback);
} }
@pragma('vm:entry-point')
static void downloadCallback(
String id, DownloadTaskStatus status, int progress) {}
@override @override
void dispose() { void dispose() {
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);

View File

@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:queue/queue.dart'; import 'package:queue/queue.dart';
import 'package:path/path.dart' as path; 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/Playback.dart';
import 'package:spotube/provider/UserPreferences.dart'; import 'package:spotube/provider/UserPreferences.dart';
import 'package:spotube/provider/YouTube.dart'; import 'package:spotube/provider/YouTube.dart';
import 'package:spotube/utils/platform.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart'; import 'package:youtube_explode_dart/youtube_explode_dart.dart';
Queue queueInstance = Queue(delay: const Duration(seconds: 5)); Queue queueInstance = Queue(delay: const Duration(seconds: 5));
@ -38,87 +36,76 @@ class Downloader with ChangeNotifier {
final logger = getLogger(Downloader); final logger = getLogger(Downloader);
Playback get _playback => ref.read(playbackProvider);
void addToQueue(Track baseTrack) async { void addToQueue(Track baseTrack) async {
if (inQueue.any((t) => t.id == baseTrack.id!)) return; if (inQueue.any((t) => t.id == baseTrack.id!)) return;
inQueue.add(baseTrack); inQueue.add(baseTrack);
currentlyRunning++; currentlyRunning++;
notifyListeners(); notifyListeners();
if (kIsMobile) {
grabberQueue.add(() async {
final track = await ref.read(playbackProvider).toSpotubeTrack(
baseTrack,
noSponsorBlock: true,
);
final filename = '${track.ytTrack.title}.mp3'; // Using android Audio Focus to keep the app run in background
_playback.mobileAudioService?.session?.setActive(true);
final url = grabberQueue.add(() async {
((await yt.videos.streamsClient.getManifest(track.ytTrack.url))) final track = await ref.read(playbackProvider).toSpotubeTrack(
.audioOnly baseTrack,
.where((audio) => audio.codec.mimeType == "audio/mp4") noSponsorBlock: true,
.withHighestBitrate() );
.url; _queue.add(() async {
await FlutterDownloader.enqueue( final cleanTitle = track.ytTrack.title.replaceAll(
savedDir: downloadPath, RegExp(r'[/\\?%*:|"<>]'),
url: url.toString(), "",
fileName: filename,
openFileFromNotification: true,
showNotification: true,
); );
}); final filename = '$cleanTitle.mp3';
} else { final file = File(path.join(downloadPath, filename));
grabberQueue.add(() async { try {
final track = await ref.read(playbackProvider).toSpotubeTrack( logger.v("[addToQueue] Download starting for ${file.path}");
baseTrack, if (file.existsSync() && await onFileExists?.call(track) != true) {
noSponsorBlock: true, return;
);
_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();
} }
}); 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() { cancelAll() {
@ -126,13 +113,9 @@ class Downloader with ChangeNotifier {
grabberQueue = Queue(); grabberQueue = Queue();
inQueue.clear(); inQueue.clear();
currentlyRunning = 0; currentlyRunning = 0;
if (kIsMobile) { _queue.cancel();
FlutterDownloader.cancelAll(); queueInstance = Queue();
} else { _queue = queueInstance;
_queue.cancel();
queueInstance = Queue();
_queue = queueInstance;
}
notifyListeners(); notifyListeners();
} }
} }

View File

@ -552,13 +552,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.0.9" 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: flutter_hooks:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -68,7 +68,6 @@ dependencies:
file_picker: ^4.6.1 file_picker: ^4.6.1
popover: ^0.2.6+3 popover: ^0.2.6+3
queue: ^3.1.0+1 queue: ^3.1.0+1
flutter_downloader: ^1.8.1
auto_size_text: ^3.0.0 auto_size_text: ^3.0.0
badges: ^2.0.3 badges: ^2.0.3