Merge pull request #122 from Hexer10/null-safety

Version 1.9.1
This commit is contained in:
Mattia 2021-04-25 23:33:06 +02:00 committed by GitHub
commit 5a94b500d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 37 additions and 19 deletions

View File

@ -1,3 +1,6 @@
## 1.9.1
- Bug fixes (due to youtube changes)
## 1.9.0 ## 1.9.0
- Support nnbd (dart 1.12) - Support nnbd (dart 1.12)
- New api: `getQuerySuggestions`: Returns the suggestions youtube provides while making a video search. - New api: `getQuerySuggestions`: Returns the suggestions youtube provides while making a video search.

View File

@ -2,6 +2,8 @@ library _youtube_explode.retry;
import 'dart:async'; import 'dart:async';
import 'package:http/http.dart';
import 'exceptions/exceptions.dart'; import 'exceptions/exceptions.dart';
/// Run the [function] each time an exception is thrown until the retryCount /// Run the [function] each time an exception is thrown until the retryCount
@ -28,7 +30,8 @@ Future<T> retry<T>(FutureOr<T> Function() function) async {
int getExceptionCost(Exception e) { int getExceptionCost(Exception e) {
if (e is TransientFailureException || if (e is TransientFailureException ||
e is FormatException || e is FormatException ||
e is SearchItemSectionException) { e is SearchItemSectionException ||
e is ClientException) {
return 1; return 1;
} }
if (e is RequestLimitExceededException) { if (e is RequestLimitExceededException) {

View File

@ -12,18 +12,19 @@ class EmbedPage {
static final _playerConfigExp = static final _playerConfigExp =
RegExp('[\'""]PLAYER_CONFIG[\'""]\\s*:\\s*(\\{.*\\})'); RegExp('[\'""]PLAYER_CONFIG[\'""]\\s*:\\s*(\\{.*\\})');
static final _playerConfigExp2 = RegExp(r'yt.setConfig\((\{.*\})'); static final _playerConfigExp2 = RegExp(r'yt.setConfig\((\{.*\})');
static final _playerConfigExp3 = RegExp(r'ytcfg.set\((\{.*\})');
final Document root; final Document root;
late final EmbedPlayerConfig? playerConfig = getPlayerConfig(); late final EmbedPlayerConfig? playerConfig = getPlayerConfig();
/// ///
String? get sourceUrl { String? get sourceUrl {
var url = root final url = root
.querySelectorAll('*[name="player_ias/base"]') .querySelectorAll('script')
.map((e) => e.attributes['src']) .map((e) => e.attributes['src'])
.where((e) => !e.isNullOrWhiteSpace) .whereNotNull()
.firstWhere((e) => e!.contains('player_ias') && e.endsWith('.js'), .firstWhereOrNull((e) => e.contains('player_ias') && e.endsWith('.js'));
orElse: () => null);
// _root.querySelector('*[name="player_ias/base"]').attributes['src']; // _root.querySelector('*[name="player_ias/base"]').attributes['src'];
if (url == null) { if (url == null) {
return null; return null;
@ -34,7 +35,8 @@ class EmbedPage {
/// ///
EmbedPlayerConfig? getPlayerConfig() { EmbedPlayerConfig? getPlayerConfig() {
var playerConfigJson = var playerConfigJson =
(_playerConfigJson ?? _playerConfigJson2)?.extractJson(); (_playerConfigJson3 ?? _playerConfigJson2 ?? _playerConfigJson)
?.extractJson();
if (playerConfigJson == null) { if (playerConfigJson == null) {
return null; return null;
} }
@ -53,6 +55,12 @@ class EmbedPage {
.map((e) => _playerConfigExp2.firstMatch(e)?.group(1)) .map((e) => _playerConfigExp2.firstMatch(e)?.group(1))
.firstWhereOrNull((e) => !e.isNullOrWhiteSpace); .firstWhereOrNull((e) => !e.isNullOrWhiteSpace);
String? get _playerConfigJson3 => root
.getElementsByTagName('script')
.map((e) => e.text)
.map((e) => _playerConfigExp3.firstMatch(e)?.group(1))
.firstWhereOrNull((e) => !e.isNullOrWhiteSpace);
/// ///
EmbedPage(this.root); EmbedPage(this.root);
@ -62,6 +70,7 @@ class EmbedPage {
/// ///
static Future<EmbedPage> get(YoutubeHttpClient httpClient, String videoId) { static Future<EmbedPage> get(YoutubeHttpClient httpClient, String videoId) {
var url = 'https://youtube.com/embed/$videoId?hl=en'; var url = 'https://youtube.com/embed/$videoId?hl=en';
// final url = 'http://localhost:8080/embed/$videoId?hl=en';
return retry(() async { return retry(() async {
var raw = await httpClient.getString(url); var raw = await httpClient.getString(url);
return EmbedPage.parse(raw); return EmbedPage.parse(raw);

View File

@ -1,6 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:youtube_explode_dart/src/retry.dart';
import '../exceptions/exceptions.dart'; import '../exceptions/exceptions.dart';
import '../videos/streams/streams.dart'; import '../videos/streams/streams.dart';
@ -12,15 +13,16 @@ class YoutubeHttpClient extends http.BaseClient {
final Map<String, String> _defaultHeaders = const { final Map<String, String> _defaultHeaders = const {
'user-agent': 'user-agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36',
'accept-language': 'en-US,en;q=1.0', 'cookie': 'CONSENT=YES+cb',
'x-youtube-client-name': '1', 'accept':
'x-youtube-client-version': '2.20200609.04.02', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'x-spf-previous': 'https://www.youtube.com/', 'accept-language': 'accept-language: en-US,en;q=0.9',
'x-spf-referer': 'https://www.youtube.com/', 'sec-fetch-dest': 'document',
'x-youtube-device': 'sec-fetch-mode': 'navigate',
'cbr=Chrome&cbrver=81.0.4044.138&ceng=WebKit&cengver=537.36' 'sec-fetch-site': 'none',
'&cos=Windows&cosver=10.0', 'sec-fetch-user': '?1',
'x-youtube-page-label': 'youtube.ytfe.desktop_20200617_1_RC1' 'sec-gpc': '1',
'upgrade-insecure-requests': '1'
}; };
/// Initialize an instance of [YoutubeHttpClient] /// Initialize an instance of [YoutubeHttpClient]
@ -99,7 +101,7 @@ class YoutubeHttpClient extends http.BaseClient {
try { try {
final request = http.Request('get', url); final request = http.Request('get', url);
request.headers['range'] = 'bytes=$i-${i + 9898989 - 1}'; request.headers['range'] = 'bytes=$i-${i + 9898989 - 1}';
final response = await send(request); final response = await retry(() => send(request));
if (validate) { if (validate) {
_validateResponse(response, response.statusCode); _validateResponse(response, response.statusCode);
} }

View File

@ -1,6 +1,6 @@
name: youtube_explode_dart name: youtube_explode_dart
description: A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key. description: A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
version: 1.9.0 version: 1.9.1
homepage: https://github.com/Hexer10/youtube_explode_dart homepage: https://github.com/Hexer10/youtube_explode_dart

View File

@ -14,7 +14,8 @@ void main() {
group('Get streams manifest of any video', () { group('Get streams manifest of any video', () {
for (final val in { for (final val in {
VideoId('9bZkp7q19f0'), // very popular VideoId('9bZkp7q19f0'), // very popular
// VideoId('SkRSXFQerZs'), // age restricted (embed allowed) - This is unplayable VideoId(
'SkRSXFQerZs'), // age restricted (embed allowed) - This is unplayable
VideoId('hySoCSoH-g8'), // age restricted (embed not allowed) VideoId('hySoCSoH-g8'), // age restricted (embed not allowed)
VideoId('_kmeFXjjGfk'), // embed not allowed (type 1) VideoId('_kmeFXjjGfk'), // embed not allowed (type 1)
VideoId('MeJVWBSsPAY'), // embed not allowed (type 2) VideoId('MeJVWBSsPAY'), // embed not allowed (type 2)