Skip to content

Commit

Permalink
feat: add download multi tracks support for mobile platform
Browse files Browse the repository at this point in the history
  • Loading branch information
KRTirtho committed Aug 14, 2022
1 parent 9edcf53 commit 0476bf7
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 35 deletions.
4 changes: 4 additions & 0 deletions .fvm/fvm_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"flutterSdkVersion": "3.0.5",
"flavors": {}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ dist
appimage-build

android/key.properties
.fvm/flutter_sdk
2 changes: 1 addition & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "oss.krtirtho.spotube"
minSdkVersion flutter.minSdkVersion
minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
Expand Down
6 changes: 5 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
</queries>

<application android:label="Spotube" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" android:usesCleartextTraffic="true" android:requestLegacyExternalStorage="true">
<activity android:name="com.ryanheise.audioservice.AudioServiceActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" >
<activity android:name="com.ryanheise.audioservice.AudioServiceActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
Expand All @@ -28,6 +28,10 @@

</activity>

<provider android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider" android:authorities="${applicationId}.flutter_downloader.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
</provider>

<service android:name="com.ryanheise.audioservice.AudioService" android:exported="false">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
Expand Down
14 changes: 13 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ 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';
Expand All @@ -26,8 +28,8 @@ void main() async {
Hive.registerAdapter(CacheTrackAdapter());
Hive.registerAdapter(CacheTrackEngagementAdapter());
Hive.registerAdapter(CacheTrackSkipSegmentAdapter());
WidgetsFlutterBinding.ensureInitialized();
if (kIsDesktop) {
WidgetsFlutterBinding.ensureInitialized();
doWhenWindowReady(() async {
final localStorage = await SharedPreferences.getInstance();
final rawSize = localStorage.getString(LocalStorageKeys.windowSizeInfo);
Expand All @@ -44,6 +46,11 @@ void main() async {
}
appWindow.show();
});
} else {
await FlutterDownloader.initialize(
debug: kDebugMode,
ignoreSsl: true,
);
}
MobileAudioService? audioServiceHandler;
runApp(ProviderScope(
Expand Down Expand Up @@ -102,8 +109,13 @@ class _SpotubeState extends ConsumerState<Spotube> with WidgetsBindingObserver {
super.initState();
SharedPreferences.getInstance().then(((value) => localStorage = value));
WidgetsBinding.instance.addObserver(this);
FlutterDownloader.registerCallback(downloadCallback);
}

@pragma('vm:entry-point')
static void downloadCallback(
String id, DownloadTaskStatus status, int progress) {}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
Expand Down
86 changes: 54 additions & 32 deletions lib/provider/Downloader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ 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;
import 'package:spotube/models/SpotubeTrack.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));
Expand All @@ -27,44 +29,64 @@ class Downloader with ChangeNotifier {
int currentlyRunning = 0;
Set<String> inQueue = {};

void addToQueue(SpotubeTrack track) {
if (inQueue.contains(track.id!)) return;
void addToQueue(SpotubeTrack track) async {
currentlyRunning++;
inQueue.add(track.id!);
notifyListeners();
_queue.add(() async {
try {
final file =
File(path.join(downloadPath, '${track.ytTrack.title}.mp3'));
if (file.existsSync() && await onFileExists?.call(track) != true) {
return;
}
file.createSync(recursive: true);
StreamManifest manifest =
await yt.videos.streamsClient.getManifest(track.ytTrack.url);
final audioStream = yt.videos.streamsClient
.get(
manifest.audioOnly
.where((audio) => audio.codec.mimeType == "audio/mp4")
.withHighestBitrate(),
)
.asBroadcastStream();
final filename = '${track.ytTrack.title}.mp3';
if (kIsMobile) {
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,
);
} else {
if (inQueue.contains(track.id!)) return;
_queue.add(() async {
try {
final file = File(path.join(downloadPath, filename));
if (file.existsSync() && await onFileExists?.call(track) != true) {
return;
}
file.createSync(recursive: true);
StreamManifest manifest =
await yt.videos.streamsClient.getManifest(track.ytTrack.url);
final audioStream = yt.videos.streamsClient
.get(
manifest.audioOnly
.where((audio) => audio.codec.mimeType == "audio/mp4")
.withHighestBitrate(),
)
.asBroadcastStream();

IOSink outputFileStream = file.openWrite();
await audioStream.pipe(outputFileStream);
await outputFileStream.flush();
} finally {
currentlyRunning--;
inQueue.remove(track.id);
notifyListeners();
}
});
IOSink outputFileStream = file.openWrite();
await audioStream.pipe(outputFileStream);
await outputFileStream.flush();
} finally {
currentlyRunning--;
inQueue.remove(track.id);
notifyListeners();
}
});
}
}

cancel() {
_queue.cancel();
_queueInstance = Queue();
_queue = _queueInstance;
cancelAll() {
if (kIsMobile) {
FlutterDownloader.cancelAll();
} else {
_queue.cancel();
_queueInstance = Queue();
_queue = _queueInstance;
}
}
}

Expand Down

0 comments on commit 0476bf7

Please sign in to comment.