parent
14e4ad7bf6
commit
7c8579d4b1
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 =
|
||||
|
|
Loading…
Reference in New Issue