mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
fix: dropped flutter_downloader deps due to slow download speed and UserDownloads not showing for anonymous
This commit is contained in:
parent
aba1ba9325
commit
307a8e21df
@ -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 ?? [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -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(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user