Version 1.7.3

Fix exceptions on some videos.
Closes #89, #88
This commit is contained in:
Mattia 2020-12-02 12:05:45 +01:00
parent 14e4ad7bf6
commit 7c8579d4b1
5 changed files with 51 additions and 40 deletions

View File

@ -10,16 +10,21 @@ import '../youtube_http_client.dart';
///
class EmbedPage {
static final _playerConfigExp =
RegExp('[\'"]PLAYER_CONFIG[\'"]\\s*:\\s*(\\{.*\\})');
RegExp('[\'""]PLAYER_CONFIG[\'""]\\s*:\\s*(\\{.*\\})');
static final _playerConfigExp2 = RegExp(r'yt.setConfig\((\{.*\})');
final Document _root;
_PlayerConfig _playerConfig;
String __playerConfigJson;
///
String get sourceUrl {
var url =
_root.querySelector('*[name="player_ias/base"]').attributes['src'];
var url = _root
.querySelectorAll('*[name="player_ias/base"]')
.map((e) => e.attributes['src'])
.where((e) => !e.isNullOrWhiteSpace)
.firstWhere((e) => e.contains('player_ias') && e.endsWith('.js'),
orElse: () => null);
// _root.querySelector('*[name="player_ias/base"]').attributes['src'];
if (url == null) {
return null;
}
@ -27,11 +32,11 @@ class EmbedPage {
}
///
_PlayerConfig get playerconfig {
_PlayerConfig get playerConfig {
if (_playerConfig != null) {
return _playerConfig;
}
var playerConfigJson = _playerConfigJson;
var playerConfigJson = _playerConfigJson ?? _playerConfigJson2;
if (playerConfigJson == null) {
return null;
}
@ -39,12 +44,18 @@ class EmbedPage {
_PlayerConfig(json.decode(playerConfigJson.extractJson()));
}
String get _playerConfigJson => __playerConfigJson ??= _root
String get _playerConfigJson => _root
.getElementsByTagName('script')
.map((e) => e.text)
.map((e) => _playerConfigExp.firstMatch(e)?.group(1))
.firstWhere((e) => !e.isNullOrWhiteSpace, orElse: () => null);
String get _playerConfigJson2 => _root
.getElementsByTagName('script')
.map((e) => e.text)
.map((e) => _playerConfigExp2.firstMatch(e)?.group(1))
.firstWhere((e) => !e.isNullOrWhiteSpace, orElse: () => null);
///
EmbedPage(this._root);

View File

@ -19,6 +19,9 @@ class WatchPage {
static final RegExp _visitorInfoLiveExp =
RegExp('VISITOR_INFO1_LIVE=([^;]+)');
static final RegExp _yscExp = RegExp('YSC=([^;]+)');
static final RegExp _playerResponseExp =
RegExp(r'var\s+ytInitialPlayerResponse\s*=\s*(\{.*\})');
static final _xsfrTokenExp = RegExp(r'"XSRF_TOKEN"\s*:\s*"(.+?)"');
final Document _root;
@ -35,8 +38,12 @@ class WatchPage {
///
String get sourceUrl {
var url =
_root.querySelector('*[name="player_ias/base"]').attributes['src'];
var url = _root
.querySelectorAll('script')
.map((e) => e.attributes['src'])
.where((e) => !e.isNullOrWhiteSpace)
.firstWhere((e) => e.contains('player_ias') && e.endsWith('.js'),
orElse: () => null);
if (url == null) {
return null;
}
@ -103,28 +110,16 @@ class WatchPage {
?.group(1)
?.extractJson()));
String _extractJson(String html, String separator) {
return _matchJson(
html.substring(html.indexOf(separator) + separator.length));
}
PlayerResponse get playerResponse => PlayerResponse.parse(_root
.querySelectorAll('script')
.map((e) => e.text)
.map((e) => null)
.map((e) => _playerResponseExp.firstMatch(e)?.group(1))
.firstWhere((e) => !e.isNullOrWhiteSpace)
.extractJson());
String _matchJson(String str) {
var bracketCount = 0;
int lastI;
for (var i = 0; i < str.length; i++) {
lastI = i;
if (str[i] == '{') {
bracketCount++;
} else if (str[i] == '}') {
bracketCount--;
} else if (str[i] == ';') {
if (bracketCount == 0) {
return str.substring(0, i);
}
}
}
return str.substring(0, lastI + 1);
}
String _extractJson(String html, String separator) =>
html.substring(html.indexOf(separator) + separator.length).extractJson();
///
WatchPage(this._root, this.visitorInfoLive, this.ysc);

View File

@ -34,7 +34,7 @@ class StreamsClient {
Future<StreamContext> _getStreamContextFromVideoInfo(VideoId videoId) async {
var embedPage = await EmbedPage.get(_httpClient, videoId.toString());
var playerConfig = embedPage.playerconfig;
var playerConfig = embedPage.playerConfig;
if (playerConfig == null) {
throw VideoUnplayableException.unplayable(videoId);
}
@ -79,21 +79,24 @@ class StreamsClient {
Future<StreamContext> _getStreamContextFromWatchPage(VideoId videoId) async {
var watchPage = await WatchPage.get(_httpClient, videoId.toString());
var playerConfig = watchPage.playerConfig;
if (playerConfig == null) {
var playerResponse =
playerConfig?.playerResponse ?? watchPage.playerResponse;
if (playerResponse == null) {
throw VideoUnplayableException.unplayable(videoId);
}
var playerResponse = playerConfig.playerResponse;
var previewVideoId = playerResponse.previewVideoId;
if (!previewVideoId.isNullOrWhiteSpace) {
throw VideoRequiresPurchaseException.preview(
videoId, VideoId(previewVideoId));
}
var playerSource = await PlayerSource.get(
_httpClient, watchPage.sourceUrl ?? playerConfig.sourceUrl);
var cipherOperations = playerSource.getCiperOperations();
var playerSourceUrl = watchPage.sourceUrl ?? playerConfig?.sourceUrl;
var playerSource = !playerSourceUrl.isNullOrWhiteSpace
? await PlayerSource.get(_httpClient, playerSourceUrl)
: null;
var cipherOperations =
playerSource?.getCiperOperations() ?? const <CipherOperation>[];
if (!playerResponse.isVideoPlayable) {
throw VideoUnplayableException.unplayable(videoId,
@ -104,7 +107,9 @@ class StreamsClient {
throw VideoUnplayableException.liveStream(videoId);
}
var streamInfoProviders = <StreamInfoProvider>[...playerResponse.streams];
var streamInfoProviders = <StreamInfoProvider>[
...playerResponse.streams,
];
var dashManifestUrl = playerResponse.dashManifestUrl;
if (!dashManifestUrl.isNullOrWhiteSpace) {

View File

@ -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.7.2
version: 1.7.3
homepage: https://github.com/Hexer10/youtube_explode_dart
environment:

View File

@ -23,7 +23,7 @@ void main() {
expect(searchQuery.content, isNotEmpty);
expect(searchQuery.relatedVideos, isNotEmpty);
expect(searchQuery.relatedQueries, isNotEmpty);
});
}, skip: 'Not supported anymore');
test('Search with no results', () async {
var query =