youtube_explode/lib/src/reverse_engineering/responses/dash_manifest.dart

88 lines
2.2 KiB
Dart
Raw Normal View History

2020-05-31 23:36:23 +02:00
import 'package:xml/xml.dart' as xml;
2020-06-03 13:18:37 +02:00
import '../../retry.dart';
2020-06-03 23:02:21 +02:00
import '../youtube_http_client.dart';
2020-06-03 13:18:37 +02:00
import 'stream_info_provider.dart';
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2020-05-31 23:36:23 +02:00
class DashManifest {
static final _urlSignatureExp = RegExp(r'/s/(.*?)(?:/|$)');
final xml.XmlDocument _root;
2020-06-22 17:40:57 +02:00
Iterable<_StreamInfo> _streams;
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2020-06-22 17:40:57 +02:00
Iterable<_StreamInfo> get streams => _streams ??= _root
2020-05-31 23:36:23 +02:00
.findElements('Representation')
.where((e) => e
.findElements('Initialization')
.first
.getAttribute('sourceURL')
.contains('sq/'))
.map((e) => _StreamInfo(e));
2020-07-16 20:02:54 +02:00
///
2020-06-22 17:40:57 +02:00
DashManifest(this._root);
2020-07-16 20:02:54 +02:00
///
2020-06-22 17:40:57 +02:00
// ignore: deprecated_member_use
2020-05-31 23:36:23 +02:00
DashManifest.parse(String raw) : _root = xml.parse(raw);
2020-07-16 20:02:54 +02:00
///
2020-06-03 13:18:37 +02:00
static Future<DashManifest> get(YoutubeHttpClient httpClient, dynamic url) {
2020-06-05 16:17:08 +02:00
return retry(() async {
2020-05-31 23:36:23 +02:00
var raw = await httpClient.getString(url);
return DashManifest.parse(raw);
});
}
2020-07-16 20:02:54 +02:00
///
2020-06-03 13:18:37 +02:00
static String getSignatureFromUrl(String url) =>
_urlSignatureExp.firstMatch(url)?.group(1);
2020-05-31 23:36:23 +02:00
}
class _StreamInfo extends StreamInfoProvider {
2020-06-05 16:17:08 +02:00
static final _contentLenExp = RegExp(r'[/\?]clen[/=](\d+)');
2020-05-31 23:36:23 +02:00
static final _containerExp = RegExp(r'mime[/=]\w*%2F([\w\d]*)');
final xml.XmlElement _root;
_StreamInfo(this._root);
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get tag => int.parse(_root.getAttribute('id'));
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
String get url => _root.getAttribute('BaseURL');
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get contentLength => int.parse(_root.getAttribute('contentLength') ??
_contentLenExp.firstMatch(url).group(1));
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get bitrate => int.parse(_root.getAttribute('bandwidth'));
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
String get container =>
Uri.decodeFull(_containerExp.firstMatch(url).group(1));
bool get isAudioOnly =>
_root.findElements('AudioChannelConfiguration').isNotEmpty;
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
String get audioCodec => isAudioOnly ? null : _root.getAttribute('codecs');
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
String get videoCodec => isAudioOnly ? _root.getAttribute('codecs') : null;
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get videoWidth => int.parse(_root.getAttribute('width'));
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get videoHeight => int.parse(_root.getAttribute('height'));
2020-06-05 16:17:08 +02:00
@override
2020-05-31 23:36:23 +02:00
int get framerate => int.parse(_root.getAttribute('framerate'));
}