From 1633db88f79e95f50df59b499747f60d962207cd Mon Sep 17 00:00:00 2001 From: Mattia Date: Fri, 2 Apr 2021 22:56:32 +0200 Subject: [PATCH] Version 1.9.1 Bug Fixes --- CHANGELOG.md | 3 +++ lib/src/retry.dart | 5 ++++- .../responses/embed_page.dart | 21 +++++++++++++----- .../youtube_http_client.dart | 22 ++++++++++--------- pubspec.yaml | 2 +- test/streams_test.dart | 3 ++- 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1893c70..e8f029f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.9.1 +- Bug fixes (due to youtube changes) + ## 1.9.0 - Support nnbd (dart 1.12) - New api: `getQuerySuggestions`: Returns the suggestions youtube provides while making a video search. diff --git a/lib/src/retry.dart b/lib/src/retry.dart index 4df2cde..fbbb4f4 100644 --- a/lib/src/retry.dart +++ b/lib/src/retry.dart @@ -2,6 +2,8 @@ library _youtube_explode.retry; import 'dart:async'; +import 'package:http/http.dart'; + import 'exceptions/exceptions.dart'; /// Run the [function] each time an exception is thrown until the retryCount @@ -28,7 +30,8 @@ Future retry(FutureOr Function() function) async { int getExceptionCost(Exception e) { if (e is TransientFailureException || e is FormatException || - e is SearchItemSectionException) { + e is SearchItemSectionException || + e is ClientException) { return 1; } if (e is RequestLimitExceededException) { diff --git a/lib/src/reverse_engineering/responses/embed_page.dart b/lib/src/reverse_engineering/responses/embed_page.dart index 636499a..5ec8a94 100644 --- a/lib/src/reverse_engineering/responses/embed_page.dart +++ b/lib/src/reverse_engineering/responses/embed_page.dart @@ -12,18 +12,19 @@ class EmbedPage { static final _playerConfigExp = RegExp('[\'""]PLAYER_CONFIG[\'""]\\s*:\\s*(\\{.*\\})'); static final _playerConfigExp2 = RegExp(r'yt.setConfig\((\{.*\})'); + static final _playerConfigExp3 = RegExp(r'ytcfg.set\((\{.*\})'); final Document root; late final EmbedPlayerConfig? playerConfig = getPlayerConfig(); /// String? get sourceUrl { - var url = root - .querySelectorAll('*[name="player_ias/base"]') + final url = root + .querySelectorAll('script') .map((e) => e.attributes['src']) - .where((e) => !e.isNullOrWhiteSpace) - .firstWhere((e) => e!.contains('player_ias') && e.endsWith('.js'), - orElse: () => null); + .whereNotNull() + .firstWhereOrNull((e) => e.contains('player_ias') && e.endsWith('.js')); + // _root.querySelector('*[name="player_ias/base"]').attributes['src']; if (url == null) { return null; @@ -34,7 +35,8 @@ class EmbedPage { /// EmbedPlayerConfig? getPlayerConfig() { var playerConfigJson = - (_playerConfigJson ?? _playerConfigJson2)?.extractJson(); + (_playerConfigJson3 ?? _playerConfigJson2 ?? _playerConfigJson) + ?.extractJson(); if (playerConfigJson == null) { return null; } @@ -53,6 +55,12 @@ class EmbedPage { .map((e) => _playerConfigExp2.firstMatch(e)?.group(1)) .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); @@ -62,6 +70,7 @@ class EmbedPage { /// static Future get(YoutubeHttpClient httpClient, String videoId) { var url = 'https://youtube.com/embed/$videoId?hl=en'; + // final url = 'http://localhost:8080/embed/$videoId?hl=en'; return retry(() async { var raw = await httpClient.getString(url); return EmbedPage.parse(raw); diff --git a/lib/src/reverse_engineering/youtube_http_client.dart b/lib/src/reverse_engineering/youtube_http_client.dart index 14ce2eb..4986909 100644 --- a/lib/src/reverse_engineering/youtube_http_client.dart +++ b/lib/src/reverse_engineering/youtube_http_client.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:http/http.dart' as http; +import 'package:youtube_explode_dart/src/retry.dart'; import '../exceptions/exceptions.dart'; import '../videos/streams/streams.dart'; @@ -12,15 +13,16 @@ class YoutubeHttpClient extends http.BaseClient { final Map _defaultHeaders = const { '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', - 'accept-language': 'en-US,en;q=1.0', - 'x-youtube-client-name': '1', - 'x-youtube-client-version': '2.20200609.04.02', - 'x-spf-previous': 'https://www.youtube.com/', - 'x-spf-referer': 'https://www.youtube.com/', - 'x-youtube-device': - 'cbr=Chrome&cbrver=81.0.4044.138&ceng=WebKit&cengver=537.36' - '&cos=Windows&cosver=10.0', - 'x-youtube-page-label': 'youtube.ytfe.desktop_20200617_1_RC1' + 'cookie': 'CONSENT=YES+cb', + 'accept': + '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', + 'accept-language': 'accept-language: en-US,en;q=0.9', + 'sec-fetch-dest': 'document', + 'sec-fetch-mode': 'navigate', + 'sec-fetch-site': 'none', + 'sec-fetch-user': '?1', + 'sec-gpc': '1', + 'upgrade-insecure-requests': '1' }; /// Initialize an instance of [YoutubeHttpClient] @@ -99,7 +101,7 @@ class YoutubeHttpClient extends http.BaseClient { try { final request = http.Request('get', url); request.headers['range'] = 'bytes=$i-${i + 9898989 - 1}'; - final response = await send(request); + final response = await retry(() => send(request)); if (validate) { _validateResponse(response, response.statusCode); } diff --git a/pubspec.yaml b/pubspec.yaml index fc14278..555e0c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ 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. -version: 1.9.0 +version: 1.9.1 homepage: https://github.com/Hexer10/youtube_explode_dart diff --git a/test/streams_test.dart b/test/streams_test.dart index 6ac5b43..886fca7 100644 --- a/test/streams_test.dart +++ b/test/streams_test.dart @@ -14,7 +14,8 @@ void main() { group('Get streams manifest of any video', () { for (final val in { 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('_kmeFXjjGfk'), // embed not allowed (type 1) VideoId('MeJVWBSsPAY'), // embed not allowed (type 2)