mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat: implement NewPipe engine
This commit is contained in:
parent
f270b2ebb9
commit
09a141b472
@ -38,6 +38,7 @@ android {
|
|||||||
ndkVersion = "27.0.12077973"
|
ndkVersion = "27.0.12077973"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
coreLibraryDesugaringEnabled true
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
@ -120,6 +121,8 @@ flutter {
|
|||||||
|
|
||||||
def glanceVersion = "1.1.1"
|
def glanceVersion = "1.1.1"
|
||||||
dependencies {
|
dependencies {
|
||||||
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
|
||||||
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||||
// other deps so just ignore
|
// other deps so just ignore
|
||||||
implementation 'com.android.support:multidex:2.0.1'
|
implementation 'com.android.support:multidex:2.0.1'
|
||||||
|
@ -51,6 +51,7 @@ import 'package:timezone/data/latest.dart' as tz;
|
|||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
import 'package:yt_dlp_dart/yt_dlp_dart.dart';
|
import 'package:yt_dlp_dart/yt_dlp_dart.dart';
|
||||||
|
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart';
|
||||||
|
|
||||||
Future<void> main(List<String> rawArgs) async {
|
Future<void> main(List<String> rawArgs) async {
|
||||||
if (rawArgs.contains("web_view_title_bar")) {
|
if (rawArgs.contains("web_view_title_bar")) {
|
||||||
@ -78,6 +79,7 @@ Future<void> main(List<String> rawArgs) async {
|
|||||||
// force High Refresh Rate on some Android devices (like One Plus)
|
// force High Refresh Rate on some Android devices (like One Plus)
|
||||||
if (kIsAndroid) {
|
if (kIsAndroid) {
|
||||||
await FlutterDisplayMode.setHighRefreshRate();
|
await FlutterDisplayMode.setHighRefreshRate();
|
||||||
|
await NewPipeExtractor.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!kIsWeb) {
|
if (!kIsWeb) {
|
||||||
|
@ -18,6 +18,7 @@ import 'package:spotube/services/sourced_track/enums.dart';
|
|||||||
import 'package:flutter/widgets.dart' hide Table, Key, View;
|
import 'package:flutter/widgets.dart' hide Table, Key, View;
|
||||||
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:spotube/services/youtube_engine/newpipe_engine.dart';
|
||||||
import 'package:spotube/services/youtube_engine/youtube_explode_engine.dart';
|
import 'package:spotube/services/youtube_engine/youtube_explode_engine.dart';
|
||||||
import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart';
|
import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart';
|
||||||
import 'package:sqlite3/sqlite3.dart';
|
import 'package:sqlite3/sqlite3.dart';
|
||||||
|
@ -34,8 +34,7 @@ enum YoutubeClientEngine {
|
|||||||
YoutubeClientEngine.youtubeExplode =>
|
YoutubeClientEngine.youtubeExplode =>
|
||||||
YouTubeExplodeEngine.isAvailableForPlatform,
|
YouTubeExplodeEngine.isAvailableForPlatform,
|
||||||
YoutubeClientEngine.ytDlp => YtDlpEngine.isAvailableForPlatform,
|
YoutubeClientEngine.ytDlp => YtDlpEngine.isAvailableForPlatform,
|
||||||
// TODO: Implement new pipe support
|
YoutubeClientEngine.newPipe => NewPipeEngine.isAvailableForPlatform,
|
||||||
YoutubeClientEngine.newPipe => false,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:spotube/models/database/database.dart';
|
import 'package:spotube/models/database/database.dart';
|
||||||
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
|
||||||
|
import 'package:spotube/services/youtube_engine/newpipe_engine.dart';
|
||||||
import 'package:spotube/services/youtube_engine/youtube_explode_engine.dart';
|
import 'package:spotube/services/youtube_engine/youtube_explode_engine.dart';
|
||||||
import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart';
|
import 'package:spotube/services/youtube_engine/yt_dlp_engine.dart';
|
||||||
|
|
||||||
@ -9,8 +10,9 @@ final youtubeEngineProvider = Provider((ref) {
|
|||||||
userPreferencesProvider.select((value) => value.youtubeClientEngine),
|
userPreferencesProvider.select((value) => value.youtubeClientEngine),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (engineMode == YoutubeClientEngine.newPipe) {
|
if (engineMode == YoutubeClientEngine.newPipe &&
|
||||||
throw UnimplementedError();
|
NewPipeEngine.isAvailableForPlatform) {
|
||||||
|
return NewPipeEngine();
|
||||||
} else if (engineMode == YoutubeClientEngine.ytDlp &&
|
} else if (engineMode == YoutubeClientEngine.ytDlp &&
|
||||||
YtDlpEngine.isAvailableForPlatform) {
|
YtDlpEngine.isAvailableForPlatform) {
|
||||||
return YtDlpEngine();
|
return YtDlpEngine();
|
||||||
|
109
lib/services/youtube_engine/newpipe_engine.dart
Normal file
109
lib/services/youtube_engine/newpipe_engine.dart
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import 'package:flutter_new_pipe_extractor/flutter_new_pipe_extractor.dart'
|
||||||
|
hide Engagement;
|
||||||
|
import 'package:spotube/services/youtube_engine/youtube_engine.dart';
|
||||||
|
import 'package:spotube/utils/platform.dart';
|
||||||
|
import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
|
||||||
|
class NewPipeEngine implements YouTubeEngine {
|
||||||
|
static bool get isAvailableForPlatform => kIsAndroid;
|
||||||
|
|
||||||
|
AudioOnlyStreamInfo _parseAudioStream(AudioStream stream, String videoId) {
|
||||||
|
return AudioOnlyStreamInfo(
|
||||||
|
VideoId(videoId),
|
||||||
|
stream.itag,
|
||||||
|
Uri.parse(stream.content),
|
||||||
|
StreamContainer.parse(stream.mediaFormat!.mimeType.split("/").last),
|
||||||
|
FileSize.unknown,
|
||||||
|
Bitrate(stream.bitrate),
|
||||||
|
stream.codec,
|
||||||
|
stream.quality,
|
||||||
|
[],
|
||||||
|
MediaType.parse(stream.mediaFormat!.mimeType),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Video _parseVideo(VideoInfo info) {
|
||||||
|
return Video(
|
||||||
|
VideoId(info.id),
|
||||||
|
info.name,
|
||||||
|
info.uploaderName,
|
||||||
|
ChannelId(info.uploaderUrl),
|
||||||
|
info.uploadDate.offsetDateTime,
|
||||||
|
info.uploadDate.offsetDateTime.toString(),
|
||||||
|
info.uploadDate.offsetDateTime,
|
||||||
|
info.description.content ?? "",
|
||||||
|
Duration(seconds: info.duration),
|
||||||
|
ThumbnailSet(info.id),
|
||||||
|
info.tags,
|
||||||
|
Engagement(
|
||||||
|
info.viewCount,
|
||||||
|
info.likeCount,
|
||||||
|
info.dislikeCount,
|
||||||
|
),
|
||||||
|
!info.streamType.name.toLowerCase().contains("live"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Video _parseVideoResult(VideoSearchResultItem info) {
|
||||||
|
final id = Uri.parse(info.url).queryParameters["v"]!;
|
||||||
|
return Video(
|
||||||
|
VideoId(id),
|
||||||
|
info.name,
|
||||||
|
info.uploaderName,
|
||||||
|
ChannelId(info.uploaderUrl),
|
||||||
|
info.uploadDate?.offsetDateTime,
|
||||||
|
info.uploadDate?.offsetDateTime.toString(),
|
||||||
|
info.uploadDate?.offsetDateTime,
|
||||||
|
info.shortDescription ?? "",
|
||||||
|
Duration(seconds: info.duration),
|
||||||
|
ThumbnailSet(id),
|
||||||
|
[],
|
||||||
|
Engagement(info.viewCount, null, null),
|
||||||
|
!info.streamType.name.toLowerCase().contains("live"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<StreamManifest> getStreamManifest(String videoId) async {
|
||||||
|
final video = await NewPipeExtractor.getVideoInfo(videoId);
|
||||||
|
|
||||||
|
final streams =
|
||||||
|
video.audioStreams.map((stream) => _parseAudioStream(stream, videoId));
|
||||||
|
|
||||||
|
return StreamManifest(streams);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Video> getVideo(String videoId) async {
|
||||||
|
final video = await NewPipeExtractor.getVideoInfo(videoId);
|
||||||
|
|
||||||
|
return _parseVideo(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<(Video, StreamManifest)> getVideoWithStreamInfo(String videoId) async {
|
||||||
|
final video = await NewPipeExtractor.getVideoInfo(videoId);
|
||||||
|
|
||||||
|
final streams =
|
||||||
|
video.audioStreams.map((stream) => _parseAudioStream(stream, videoId));
|
||||||
|
|
||||||
|
return (_parseVideo(video), StreamManifest(streams));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<Video>> searchVideos(String query) async {
|
||||||
|
final results = await NewPipeExtractor.search(
|
||||||
|
query,
|
||||||
|
contentFilters: [SearchContentFilters.musicSongs],
|
||||||
|
);
|
||||||
|
|
||||||
|
final resultsWithVideos = results
|
||||||
|
.whereType<VideoSearchResultItem>()
|
||||||
|
.map((e) => _parseVideoResult(e))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return resultsWithVideos;
|
||||||
|
}
|
||||||
|
}
|
13
pubspec.lock
13
pubspec.lock
@ -914,6 +914,13 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.4.3"
|
version: "2.4.3"
|
||||||
|
flutter_new_pipe_extractor:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "../flutter_new_pipe_extractor"
|
||||||
|
relative: true
|
||||||
|
source: path
|
||||||
|
version: "0.1.0"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1210,13 +1217,13 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.1"
|
version: "4.1.2"
|
||||||
image:
|
image:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -144,6 +144,9 @@ dependencies:
|
|||||||
git:
|
git:
|
||||||
url: https://github.com/KRTirtho/yt_dlp_dart.git
|
url: https://github.com/KRTirtho/yt_dlp_dart.git
|
||||||
ref: e2d82305fab18566408d6f8758361017d1640c3d
|
ref: e2d82305fab18566408d6f8758361017d1640c3d
|
||||||
|
flutter_new_pipe_extractor:
|
||||||
|
path: ../flutter_new_pipe_extractor
|
||||||
|
http_parser: ^4.1.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.13
|
build_runner: ^2.4.13
|
||||||
|
Loading…
Reference in New Issue
Block a user