2021-03-11 23:28:51 +01:00
|
|
|
import 'dart:convert';
|
|
|
|
|
2021-02-26 16:08:48 +01:00
|
|
|
import '../../youtube_explode_dart.dart';
|
|
|
|
import '../retry.dart';
|
2020-11-01 15:05:19 +01:00
|
|
|
import '../reverse_engineering/responses/search_page.dart';
|
2020-06-06 11:28:36 +02:00
|
|
|
import '../reverse_engineering/youtube_http_client.dart';
|
2020-09-21 17:34:03 +02:00
|
|
|
import 'base_search_content.dart';
|
2021-02-26 16:08:48 +01:00
|
|
|
import 'search_list.dart';
|
2021-02-26 21:37:14 +01:00
|
|
|
import 'search_query.dart';
|
2020-06-06 11:28:36 +02:00
|
|
|
|
|
|
|
/// YouTube search queries.
|
|
|
|
class SearchClient {
|
|
|
|
final YoutubeHttpClient _httpClient;
|
|
|
|
|
|
|
|
/// Initializes an instance of [SearchClient]
|
|
|
|
SearchClient(this._httpClient);
|
|
|
|
|
2021-02-26 16:08:48 +01:00
|
|
|
/// Enumerates videos returned by the specified search query
|
|
|
|
/// (from the video search page).
|
|
|
|
/// The videos are sent in batch of 20 videos.
|
|
|
|
/// You [SearchList.nextPage] to get the next batch of videos.
|
|
|
|
Future<SearchList> getVideos(String searchQuery) {
|
2021-03-11 14:20:10 +01:00
|
|
|
var stream = getVideosFromPage(searchQuery).cast<SearchVideo>();
|
2021-02-26 16:08:48 +01:00
|
|
|
return SearchList.create(stream);
|
2020-06-06 11:28:36 +02:00
|
|
|
}
|
2020-06-13 22:54:53 +02:00
|
|
|
|
2020-09-21 17:34:03 +02:00
|
|
|
/// Enumerates videos returned by the specified search query
|
|
|
|
/// (from the video search page).
|
|
|
|
/// Contains only instances of [SearchVideo] or [SearchPlaylist]
|
2021-02-26 16:08:48 +01:00
|
|
|
Stream<BaseSearchContent> getVideosFromPage(String searchQuery,
|
|
|
|
{bool onlyVideos = true}) async* {
|
2021-03-11 14:20:10 +01:00
|
|
|
SearchPage? page =
|
2021-02-26 16:08:48 +01:00
|
|
|
await retry(() async => SearchPage.get(_httpClient, searchQuery));
|
|
|
|
if (onlyVideos) {
|
|
|
|
yield* Stream.fromIterable(
|
2021-03-11 14:20:10 +01:00
|
|
|
page!.initialData.searchContent.whereType<SearchVideo>());
|
2021-02-26 16:08:48 +01:00
|
|
|
} else {
|
2021-03-11 14:20:10 +01:00
|
|
|
yield* Stream.fromIterable(page!.initialData.searchContent);
|
2021-02-26 16:08:48 +01:00
|
|
|
}
|
2020-09-21 17:34:03 +02:00
|
|
|
|
|
|
|
// ignore: literal_only_boolean_expressions
|
|
|
|
while (true) {
|
2021-03-11 14:20:10 +01:00
|
|
|
page = await page!.nextPage(_httpClient);
|
2020-09-21 17:34:03 +02:00
|
|
|
if (page == null) {
|
|
|
|
return;
|
|
|
|
}
|
2021-02-26 16:08:48 +01:00
|
|
|
|
|
|
|
if (onlyVideos) {
|
|
|
|
yield* Stream.fromIterable(
|
|
|
|
page.initialData.searchContent.whereType<SearchVideo>());
|
|
|
|
} else {
|
|
|
|
yield* Stream.fromIterable(page.initialData.searchContent);
|
|
|
|
}
|
2020-09-21 17:34:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-11 23:28:51 +01:00
|
|
|
/// Returns the suggestions youtube provide while search on the page.
|
|
|
|
Future<List<String>> getQuerySuggestions(String query) async {
|
|
|
|
final request = await _httpClient.get(
|
|
|
|
'https://suggestqueries-clients6.youtube.com/complete/search?client=youtube&hl=en&gl=en&q=${Uri.encodeComponent(query)}&callback=func');
|
|
|
|
final body = request.body;
|
|
|
|
final startIndex = body.indexOf('func(');
|
|
|
|
final jsonStr = body.substring(startIndex + 5, body.length - 1);
|
|
|
|
final data = json.decode(jsonStr) as List<dynamic>;
|
|
|
|
final suggestions = data[1] as List<dynamic>;
|
|
|
|
return suggestions.map((e) => e[0]).toList().cast<String>();
|
|
|
|
}
|
|
|
|
|
2020-06-13 22:54:53 +02:00
|
|
|
/// Queries to YouTube to get the results.
|
2020-10-17 22:09:52 +02:00
|
|
|
@Deprecated('Use getVideosFromPage instead - '
|
|
|
|
'Should be used only to get related videos')
|
2020-06-13 22:54:53 +02:00
|
|
|
Future<SearchQuery> queryFromPage(String searchQuery) =>
|
|
|
|
SearchQuery.search(_httpClient, searchQuery);
|
2020-06-06 11:28:36 +02:00
|
|
|
}
|
2020-09-21 17:34:03 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
channelId = ChannelId.fromString(channelId);
|
|
|
|
var page = await ChannelUploadPage.get(
|
|
|
|
_httpClient, channelId.value, videoSorting.code);
|
|
|
|
yield* Stream.fromIterable(page.initialData.uploads);
|
|
|
|
|
|
|
|
// ignore: literal_only_boolean_expressions
|
|
|
|
while (true) {
|
|
|
|
page = await page.nextPage(_httpClient);
|
|
|
|
if (page == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
yield* Stream.fromIterable(page.initialData.uploads);
|
|
|
|
}
|
|
|
|
*/
|