parent
51e40f27cc
commit
9c8e9630ab
|
@ -3,6 +3,9 @@
|
|||
- Only throw custom exceptions from the library.
|
||||
- `getUploadsFromPage` no longer throws.
|
||||
|
||||
## 1.6.2
|
||||
- Bug fixes: #80
|
||||
|
||||
## 1.6.1
|
||||
- Add thumbnail to `SearchVideo` thanks to @shinyford !
|
||||
|
||||
|
|
|
@ -29,6 +29,32 @@ extension StringUtility on String {
|
|||
|
||||
/// Strips out all non digit characters.
|
||||
String stripNonDigits() => replaceAll(_exp, '');
|
||||
|
||||
///
|
||||
String extractJson() {
|
||||
var buffer = StringBuffer();
|
||||
var depth = 0;
|
||||
|
||||
for (var i = 0; i < length; i++) {
|
||||
var ch = this[i];
|
||||
var chPrv = i > 0 ? this[i - 1] : '';
|
||||
|
||||
buffer.write(ch);
|
||||
|
||||
if (ch == '{' && chPrv != '\\') {
|
||||
depth++;
|
||||
} else if (ch == '}' && chPrv != '\\') {
|
||||
depth--;
|
||||
}
|
||||
|
||||
if (depth == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// List decipher utility.
|
||||
|
|
|
@ -7,14 +7,25 @@ import '../../extensions/helpers_extension.dart';
|
|||
import '../../retry.dart';
|
||||
import '../youtube_http_client.dart';
|
||||
|
||||
|
||||
///
|
||||
class EmbedPage {
|
||||
static final _playerConfigExp = RegExp(r"'PLAYER_CONFIG':\s*(\{.*\})\}");
|
||||
static final _playerConfigExp =
|
||||
RegExp('[\'"]PLAYER_CONFIG[\'"]\\s*:\\s*(\\{.*\\})');
|
||||
|
||||
final Document _root;
|
||||
_PlayerConfig _playerConfig;
|
||||
String __playerConfigJson;
|
||||
|
||||
///
|
||||
String get sourceUrl {
|
||||
var url = _root.querySelector('*[name="player_ias/base"]').attributes['src'];
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
return 'https://youtube.com$url';
|
||||
}
|
||||
|
||||
///
|
||||
_PlayerConfig get playerconfig {
|
||||
if (_playerConfig != null) {
|
||||
|
@ -24,7 +35,8 @@ class EmbedPage {
|
|||
if (playerConfigJson == null) {
|
||||
return null;
|
||||
}
|
||||
return _playerConfig = _PlayerConfig(json.decode(playerConfigJson));
|
||||
return _playerConfig =
|
||||
_PlayerConfig(json.decode(playerConfigJson.extractJson()));
|
||||
}
|
||||
|
||||
String get _playerConfigJson => __playerConfigJson ??= _root
|
||||
|
|
|
@ -30,7 +30,7 @@ class PlayerSource {
|
|||
var val = RegExp(r'(?<=invalid namespace.*?;[\w\s]+=)\d+')
|
||||
.stringMatch(_root)
|
||||
?.nullIfWhitespace ??
|
||||
RegExp(r'(?<=this\.signatureTimestamp=)\d+')
|
||||
RegExp(r'(?<=signatureTimestamp[=\:])\d+')
|
||||
.stringMatch(_root)
|
||||
?.nullIfWhitespace;
|
||||
if (val == null) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:html/dom.dart';
|
||||
import 'package:html/parser.dart' as parser;
|
||||
|
||||
|
@ -35,6 +33,16 @@ class WatchPage {
|
|||
String _xsfrToken;
|
||||
_PlayerConfig _playerConfig;
|
||||
|
||||
///
|
||||
String get sourceUrl {
|
||||
var url =
|
||||
_root.querySelector('*[name="player_ias/base"]').attributes['src'];
|
||||
if (url == null) {
|
||||
return null;
|
||||
}
|
||||
return 'https://youtube.com$url';
|
||||
}
|
||||
|
||||
///
|
||||
_InitialData get initialData =>
|
||||
_initialData ??= _InitialData(WatchPageId.fromRawJson(_extractJson(
|
||||
|
@ -86,13 +94,14 @@ class WatchPage {
|
|||
?.nullIfWhitespace ??
|
||||
'0');
|
||||
|
||||
static final _playerConfigExp = RegExp(r'ytplayer\.config\s*=\s*(\{.*\}\});');
|
||||
static final _playerConfigExp = RegExp(r'ytplayer\.config\s*=\s*(\{.*\})');
|
||||
|
||||
///
|
||||
_PlayerConfig get playerConfig => _playerConfig ??= _PlayerConfig(
|
||||
PlayerConfigJson.fromRawJson(_playerConfigExp
|
||||
.firstMatch(_root.getElementsByTagName('html').first.text)
|
||||
?.group(1)));
|
||||
?.group(1)
|
||||
?.extractJson()));
|
||||
|
||||
String _extractJson(String html, String separator) {
|
||||
return _matchJson(
|
||||
|
|
|
@ -11,7 +11,7 @@ class YoutubeHttpClient extends http.BaseClient {
|
|||
|
||||
final Map<String, String> _defaultHeaders = const {
|
||||
'user-agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 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',
|
||||
'x-youtube-client-name': '1',
|
||||
'x-youtube-client-version': '2.20200609.04.02',
|
||||
|
|
|
@ -39,8 +39,8 @@ class StreamsClient {
|
|||
throw VideoUnplayableException.unplayable(videoId);
|
||||
}
|
||||
|
||||
var playerSource =
|
||||
await PlayerSource.get(_httpClient, playerConfig.sourceUrl);
|
||||
var playerSource = await PlayerSource.get(
|
||||
_httpClient, embedPage.sourceUrl ?? playerConfig.sourceUrl);
|
||||
var cipherOperations = playerSource.getCiperOperations();
|
||||
|
||||
var videoInfoResponse = await VideoInfoResponse.get(
|
||||
|
@ -91,8 +91,8 @@ class StreamsClient {
|
|||
videoId, VideoId(previewVideoId));
|
||||
}
|
||||
|
||||
var playerSource =
|
||||
await PlayerSource.get(_httpClient, playerConfig.sourceUrl);
|
||||
var playerSource = await PlayerSource.get(
|
||||
_httpClient, watchPage.sourceUrl ?? playerConfig.sourceUrl);
|
||||
var cipherOperations = playerSource.getCiperOperations();
|
||||
|
||||
if (!playerResponse.isVideoPlayable) {
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
|
@ -35,12 +35,12 @@ void main() {
|
|||
});
|
||||
|
||||
test('Search youtube videos have thumbnails', () async {
|
||||
var searchQuery = await yt.search.queryFromPage('hello');
|
||||
expect(searchQuery.content.first, isA<SearchVideo>());
|
||||
var searchQuery = await yt.search.queryFromPage('hello');
|
||||
expect(searchQuery.content.first, isA<SearchVideo>());
|
||||
|
||||
var video = searchQuery.content.first as SearchVideo;
|
||||
expect(video.videoThumbnails, isNotEmpty);
|
||||
});
|
||||
var video = searchQuery.content.first as SearchVideo;
|
||||
expect(video.videoThumbnails, isNotEmpty);
|
||||
});
|
||||
|
||||
test('Search youtube videos from search page (stream)', () async {
|
||||
var query = await yt.search.getVideosFromPage('hello').take(30).toList();
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
@ -53,5 +53,5 @@ void main() {
|
|||
}
|
||||
});
|
||||
}
|
||||
}, skip: 'Occasionally may fail with certain videos');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ import 'package:youtube_explode_dart/youtube_explode_dart.dart';
|
|||
|
||||
void main() {
|
||||
YoutubeExplode yt;
|
||||
setUp(() {
|
||||
setUpAll(() {
|
||||
yt = YoutubeExplode();
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tearDownAll(() {
|
||||
yt.close();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue