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) {
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<Artist>(
track.artists ?? [],
trailing: const SizedBox(
width: 30,
height: 30,
child: CircularProgressIndicator.adaptive(),
),
),
);
},
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/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(),
]),
),
),
),

View File

@ -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,

View File

@ -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<Spotube> 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);

View File

@ -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();
}
}

View File

@ -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:

View File

@ -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