youtube_explode/lib/src/reverse_engineering/clients/video_info_client.dart

151 lines
3.6 KiB
Dart
Raw Normal View History

2021-07-23 12:54:29 +02:00
import 'package:freezed_annotation/freezed_annotation.dart';
2020-05-31 23:36:23 +02:00
import 'package:http_parser/http_parser.dart';
2020-06-03 23:02:21 +02:00
import '../../exceptions/exceptions.dart';
2021-03-11 14:20:10 +01:00
import '../../extensions/helpers_extension.dart';
2020-06-03 23:02:21 +02:00
import '../../retry.dart';
2021-07-21 02:06:02 +02:00
import '../models/stream_info_provider.dart';
2021-07-23 12:54:29 +02:00
import '../player/player_response.dart';
import '../youtube_http_client.dart';
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
///
2021-07-23 12:54:29 +02:00
@Deprecated('This endpoint is not supported anymore.')
class VideoInfoClient {
2021-03-11 14:20:10 +01:00
final Map<String, String> root;
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final String status = root['status']!;
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final bool isVideoAvailable = status.toLowerCase() != 'fail';
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final PlayerResponse playerResponse =
PlayerResponse.parse(root['player_response']!);
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final Iterable<_StreamInfo> muxedStreams =
root['url_encoded_fmt_stream_map']
2020-06-22 17:40:57 +02:00
?.split(',')
2021-03-11 14:20:10 +01:00
.map(Uri.splitQueryString)
.map((e) => _StreamInfo(e)) ??
2020-06-22 17:40:57 +02:00
const [];
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final Iterable<_StreamInfo> adaptiveStreams = root['adaptive_fmts']
?.split(',')
.map(Uri.splitQueryString)
.map((e) => _StreamInfo(e)) ??
const [];
2020-06-22 17:40:57 +02:00
2020-07-16 20:02:54 +02:00
///
2021-03-11 14:20:10 +01:00
late final Iterable<_StreamInfo> streams = [
...muxedStreams,
...adaptiveStreams
];
2020-05-31 23:36:23 +02:00
2020-07-16 20:02:54 +02:00
///
VideoInfoClient(this.root);
2020-06-03 13:18:37 +02:00
2020-07-16 20:02:54 +02:00
///
VideoInfoClient.parse(String raw) : root = Uri.splitQueryString(raw);
2020-06-03 13:18:37 +02:00
2020-07-16 20:02:54 +02:00
///
2021-07-23 12:54:29 +02:00
@alwaysThrows
static Future<VideoInfoClient> get(
2020-06-03 13:18:37 +02:00
YoutubeHttpClient httpClient, String videoId,
2021-03-11 14:20:10 +01:00
[String? sts]) {
2020-06-03 13:18:37 +02:00
var eurl = Uri.encodeFull('https://youtube.googleapis.com/v/$videoId');
2021-06-18 17:14:55 +02:00
final url = Uri(
scheme: 'https',
host: 'youtube.com',
path: '/get_video_info',
queryParameters: {
'video_id': videoId,
'el': 'embedded',
'eurl': eurl,
'hl': 'en',
if (sts != null) 'sts': sts,
'html5': '1',
'c': 'TVHTML5',
'cver': '6.20180913'
});
2020-06-03 13:18:37 +02:00
return retry(() async {
var raw = await httpClient.getString(url);
var result = VideoInfoClient.parse(raw);
2020-06-03 13:18:37 +02:00
if (!result.isVideoAvailable || !result.playerResponse.isVideoAvailable) {
throw VideoUnplayableException(videoId);
}
return result;
});
}
2020-05-31 23:36:23 +02:00
}
class _StreamInfo extends StreamInfoProvider {
2021-03-11 14:20:10 +01:00
final Map<String, String> root;
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int tag = int.parse(root['itag']!);
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String url = root['url']!;
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String? signature = root['s'];
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String? signatureParameter = root['sp'];
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int? contentLength = int.tryParse(root['clen'] ??
StreamInfoProvider.contentLenExp.firstMatch(url)?.group(1) ??
'');
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int? bitrate = int.tryParse(root['bitrate'] ?? '');
2020-05-31 23:36:23 +02:00
2021-03-11 14:20:10 +01:00
late final MediaType mimeType = MediaType.parse(root['type']!);
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String container = mimeType.subtype;
2020-05-31 23:36:23 +02:00
2021-03-11 14:20:10 +01:00
late final List<String> codecs = mimeType.parameters['codecs']!
.split(',')
.map((e) => e.trim())
.toList()
.cast<String>();
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String audioCodec = codecs.last;
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String? videoCodec = isAudioOnly ? null : codecs.first;
2020-05-31 23:36:23 +02:00
2021-03-11 14:20:10 +01:00
late final bool isAudioOnly = mimeType.type == 'audio';
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final String? videoQualityLabel = root['quality_label'];
2020-05-31 23:36:23 +02:00
2021-03-11 14:20:10 +01:00
late final List<int>? _size = root
.getT<String>('size')
?.split(',')
.map((e) => int.tryParse(e))
.toList()
.cast<int>();
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int? videoWidth = _size?.first;
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int? videoHeight = _size?.last;
2020-05-31 23:36:23 +02:00
@override
2021-03-11 14:20:10 +01:00
late final int? framerate = int.tryParse(root['fps'] ?? '');
2020-06-22 17:40:57 +02:00
2021-03-11 14:20:10 +01:00
_StreamInfo(this.root);
}