mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
feat: use isolate for youtube_explode engine
This commit is contained in:
parent
e83a4bb388
commit
2f304fa943
@ -110,4 +110,7 @@ class NewPipeEngine implements YouTubeEngine {
|
||||
|
||||
return resultsWithVideos;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
}
|
||||
|
@ -11,4 +11,6 @@ abstract interface class YouTubeEngine {
|
||||
Future<StreamManifest> getStreamManifest(String videoId);
|
||||
Future<(Video, StreamManifest)> getVideoWithStreamInfo(String videoId);
|
||||
Future<List<Video>> searchVideos(String query);
|
||||
|
||||
void dispose();
|
||||
}
|
||||
|
@ -1,8 +1,136 @@
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:spotube/services/youtube_engine/youtube_engine.dart';
|
||||
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
/// It contains methods that are computationally expensive
|
||||
class IsolatedYoutubeExplode {
|
||||
final Isolate _isolate;
|
||||
final SendPort _sendPort;
|
||||
final ReceivePort _receivePort;
|
||||
|
||||
IsolatedYoutubeExplode._(
|
||||
Isolate isolate,
|
||||
ReceivePort receivePort,
|
||||
SendPort sendPort,
|
||||
) : _isolate = isolate,
|
||||
_receivePort = receivePort,
|
||||
_sendPort = sendPort;
|
||||
|
||||
static IsolatedYoutubeExplode? _instance;
|
||||
|
||||
static IsolatedYoutubeExplode get instance => _instance!;
|
||||
|
||||
static bool get isInitialized => _instance != null;
|
||||
|
||||
static Future<void> initialize() async {
|
||||
if (_instance != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final completer = Completer<SendPort>();
|
||||
|
||||
final receivePort = ReceivePort();
|
||||
|
||||
/// Listen for the main isolate to set the main port
|
||||
final subscription = receivePort.listen((message) {
|
||||
if (message is SendPort) {
|
||||
completer.complete(message);
|
||||
}
|
||||
});
|
||||
|
||||
final isolate = await Isolate.spawn(_isolateEntry, receivePort.sendPort);
|
||||
|
||||
_instance = IsolatedYoutubeExplode._(
|
||||
isolate,
|
||||
receivePort,
|
||||
await completer.future,
|
||||
);
|
||||
|
||||
if (completer.isCompleted) {
|
||||
subscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
static void _isolateEntry(SendPort mainSendPort) {
|
||||
final receivePort = ReceivePort();
|
||||
final youtubeExplode = YoutubeExplode();
|
||||
|
||||
/// Send the main port to the main isolate
|
||||
mainSendPort.send(receivePort.sendPort);
|
||||
|
||||
receivePort.listen((message) async {
|
||||
final SendPort replyPort = message[0];
|
||||
final String methodName = message[1];
|
||||
final List<dynamic> arguments = message[2];
|
||||
|
||||
// Run the requested method on YoutubeExplode
|
||||
var result = switch (methodName) {
|
||||
"search" => youtubeExplode.search
|
||||
.search(
|
||||
arguments[0] as String,
|
||||
filter: arguments.elementAtOrNull(1) ?? TypeFilters.video,
|
||||
)
|
||||
.then((s) => s.toList()),
|
||||
"video" => youtubeExplode.videos.get(arguments[0] as String),
|
||||
"manifest" => youtubeExplode.videos.streamsClient.getManifest(
|
||||
arguments[0] as String,
|
||||
requireWatchPage: arguments.elementAtOrNull(1) ?? true,
|
||||
ytClients: arguments.elementAtOrNull(2) as List<YoutubeApiClient>?,
|
||||
),
|
||||
_ => throw ArgumentError('Invalid method name: $methodName'),
|
||||
};
|
||||
|
||||
replyPort.send(await result);
|
||||
});
|
||||
}
|
||||
|
||||
Future<T> _runMethod<T>(String methodName, List<dynamic> args) {
|
||||
final completer = Completer<T>();
|
||||
final responsePort = ReceivePort();
|
||||
|
||||
responsePort.listen((message) {
|
||||
completer.complete(message as T);
|
||||
responsePort.close();
|
||||
});
|
||||
|
||||
_sendPort.send([responsePort.sendPort, methodName, args]);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
Future<List<Video>> search(
|
||||
String query, {
|
||||
SearchFilter? filter,
|
||||
}) async {
|
||||
return _runMethod<List<Video>>("search", [query]);
|
||||
}
|
||||
|
||||
Future<Video> video(String videoId) async {
|
||||
return _runMethod<Video>("video", [videoId]);
|
||||
}
|
||||
|
||||
Future<StreamManifest> manifest(
|
||||
String videoId, {
|
||||
bool requireWatchPage = false,
|
||||
List<YoutubeApiClient>? ytClients,
|
||||
}) async {
|
||||
return _runMethod<StreamManifest>("manifest", [
|
||||
videoId,
|
||||
requireWatchPage,
|
||||
ytClients,
|
||||
]);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_receivePort.close();
|
||||
_isolate.kill(priority: Isolate.immediate);
|
||||
}
|
||||
}
|
||||
|
||||
class YouTubeExplodeEngine implements YouTubeEngine {
|
||||
static final YoutubeExplode _youtubeExplode = YoutubeExplode();
|
||||
static final _youtubeExplode = IsolatedYoutubeExplode.instance;
|
||||
|
||||
static bool get isAvailableForPlatform => true;
|
||||
|
||||
@ -12,8 +140,9 @@ class YouTubeExplodeEngine implements YouTubeEngine {
|
||||
|
||||
@override
|
||||
Future<StreamManifest> getStreamManifest(String videoId) async {
|
||||
final streamManifest =
|
||||
await _youtubeExplode.videos.streamsClient.getManifest(
|
||||
await IsolatedYoutubeExplode.initialize();
|
||||
|
||||
final streamManifest = await _youtubeExplode.manifest(
|
||||
videoId,
|
||||
requireWatchPage: false,
|
||||
ytClients: [
|
||||
@ -47,12 +176,15 @@ class YouTubeExplodeEngine implements YouTubeEngine {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Video> getVideo(String videoId) {
|
||||
return _youtubeExplode.videos.get(videoId);
|
||||
Future<Video> getVideo(String videoId) async {
|
||||
await IsolatedYoutubeExplode.initialize();
|
||||
return _youtubeExplode.video(videoId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<(Video, StreamManifest)> getVideoWithStreamInfo(String videoId) async {
|
||||
await IsolatedYoutubeExplode.initialize();
|
||||
|
||||
final video = await getVideo(videoId);
|
||||
final streamManifest = await getStreamManifest(videoId);
|
||||
|
||||
@ -60,12 +192,19 @@ class YouTubeExplodeEngine implements YouTubeEngine {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Video>> searchVideos(String query) {
|
||||
return _youtubeExplode.search
|
||||
Future<List<Video>> searchVideos(String query) async {
|
||||
await IsolatedYoutubeExplode.initialize();
|
||||
|
||||
return _youtubeExplode
|
||||
.search(
|
||||
query,
|
||||
filter: TypeFilters.video,
|
||||
)
|
||||
.then((searchList) => searchList.toList());
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
IsolatedYoutubeExplode.instance.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -146,4 +146,7 @@ class YtDlpEngine implements YouTubeEngine {
|
||||
|
||||
return json.map((e) => _parseInfo(e)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user