109 lines
2.7 KiB
Dart
109 lines
2.7 KiB
Dart
|
import 'package:http_parser/http_parser.dart';
|
||
|
import 'package:youtube_explode_dart/src/reverse_engineering/responses/player_response.dart';
|
||
|
import 'package:youtube_explode_dart/src/reverse_engineering/responses/stream_info_provider.dart';
|
||
|
|
||
|
class VideoInfoResponse {
|
||
|
final Map<String, String> _root;
|
||
|
|
||
|
VideoInfoResponse(this._root);
|
||
|
|
||
|
String get status => _root['status'];
|
||
|
|
||
|
bool get isVideoAvailable => status.toLowerCase() == 'fail';
|
||
|
|
||
|
PlayerResponse get playerResponse =>
|
||
|
PlayerResponse.parse(_root['player_response']);
|
||
|
|
||
|
Iterable<_StreamInfo> get muxedStreams =>
|
||
|
_root['url_encoded_fmt_stream_map']
|
||
|
?.split(',')
|
||
|
?.map(Uri.splitQueryString)
|
||
|
?.map((e) => _StreamInfo(e)) ??
|
||
|
const [];
|
||
|
|
||
|
Iterable<_StreamInfo> get adaptiveStreams =>
|
||
|
_root['adaptive_fmts']
|
||
|
?.split(',')
|
||
|
?.map(Uri.splitQueryString)
|
||
|
?.map((e) => _StreamInfo(e)) ??
|
||
|
const [];
|
||
|
|
||
|
Iterable<_StreamInfo> get streams => [...muxedStreams, ...adaptiveStreams];
|
||
|
}
|
||
|
|
||
|
class _StreamInfo extends StreamInfoProvider {
|
||
|
final Map<String, String> _root;
|
||
|
|
||
|
_StreamInfo(this._root);
|
||
|
|
||
|
@override
|
||
|
int get tag => int.parse(_root['itag']);
|
||
|
|
||
|
@override
|
||
|
String get url => _root['url'];
|
||
|
|
||
|
@override
|
||
|
String get signature => _root['s'];
|
||
|
|
||
|
@override
|
||
|
String get signatureParameter => _root['sp'];
|
||
|
|
||
|
@override
|
||
|
int get contentLength => int.tryParse(_root['clen'] ??
|
||
|
StreamInfoProvider.contentLenExp.firstMatch(url).group(1));
|
||
|
|
||
|
@override
|
||
|
int get bitrate => int.parse(_root['bitrate']);
|
||
|
|
||
|
MediaType get mimeType => MediaType.parse(_root["type"]);
|
||
|
|
||
|
@override
|
||
|
String get container => mimeType.subtype;
|
||
|
|
||
|
List<String> get codecs =>
|
||
|
mimeType.parameters['codecs'].split(',').map((e) => e.trim());
|
||
|
|
||
|
@override
|
||
|
String get audioCodec => codecs.last;
|
||
|
|
||
|
@override
|
||
|
String get videoCodec => isAudioOnly ? null : codecs.first;
|
||
|
|
||
|
bool get isAudioOnly => mimeType.type == 'audio';
|
||
|
|
||
|
@override
|
||
|
// TODO: implement videoQualityLabel
|
||
|
String get videoQualityLabel => _root['quality_label'];
|
||
|
|
||
|
List<int> get _size =>
|
||
|
_root['size'].split(',').map((e) => int.tryParse(e ?? ''));
|
||
|
|
||
|
@override
|
||
|
int get videoWidth => _size.first;
|
||
|
|
||
|
@override
|
||
|
int get videoHeight => _size.last;
|
||
|
|
||
|
@override
|
||
|
int get framerate => int.tryParse(_root['fps'] ?? '');
|
||
|
}
|
||
|
|
||
|
extension on String {
|
||
|
String get nullIfWhitespace => trim().isEmpty ? null : this;
|
||
|
|
||
|
bool get isNullOrWhiteSpace {
|
||
|
if (this == null) {
|
||
|
return true;
|
||
|
}
|
||
|
if (trim().isEmpty) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
String substringUntil(String separator) => substring(0, indexOf(separator));
|
||
|
|
||
|
String substringAfter(String separator) =>
|
||
|
substring(indexOf(separator) + length);
|
||
|
}
|