Ignore content-length; Workaround for #162

This commit is contained in:
Mattia 2021-09-01 10:43:21 +02:00
parent 994ad62cc4
commit 214a5f1803
7 changed files with 27 additions and 161 deletions

View File

@ -26,14 +26,14 @@ class EmbeddedPlayerClient {
late final Iterable<_StreamInfo> muxedStreams = root late final Iterable<_StreamInfo> muxedStreams = root
.get('streamingData') .get('streamingData')
?.getList('formats') ?.getList('formats')
?.map((e) => _StreamInfo(e)) ?? ?.map((e) => _StreamInfo(e, StreamSource.muxed)) ??
const []; const [];
/// ///
late final Iterable<_StreamInfo> adaptiveStreams = root late final Iterable<_StreamInfo> adaptiveStreams = root
.get('streamingData') .get('streamingData')
?.getList('adaptiveFormats') ?.getList('adaptiveFormats')
?.map((e) => _StreamInfo(e)) ?? ?.map((e) => _StreamInfo(e, StreamSource.adaptive)) ??
const []; const [];
/// ///
@ -131,5 +131,9 @@ class _StreamInfo extends StreamInfoProvider {
@override @override
late final int? framerate = root['fps'] ?? 0; late final int? framerate = root['fps'] ?? 0;
_StreamInfo(this.root);
@override
final StreamSource source;
_StreamInfo(this.root, this.source);
} }

View File

@ -1,150 +0,0 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:http_parser/http_parser.dart';
import '../../exceptions/exceptions.dart';
import '../../extensions/helpers_extension.dart';
import '../../retry.dart';
import '../models/stream_info_provider.dart';
import '../player/player_response.dart';
import '../youtube_http_client.dart';
///
///
@Deprecated('This endpoint is not supported anymore.')
class VideoInfoClient {
final Map<String, String> root;
///
late final String status = root['status']!;
///
late final bool isVideoAvailable = status.toLowerCase() != 'fail';
///
late final PlayerResponse playerResponse =
PlayerResponse.parse(root['player_response']!);
///
late final Iterable<_StreamInfo> muxedStreams =
root['url_encoded_fmt_stream_map']
?.split(',')
.map(Uri.splitQueryString)
.map((e) => _StreamInfo(e)) ??
const [];
///
late final Iterable<_StreamInfo> adaptiveStreams = root['adaptive_fmts']
?.split(',')
.map(Uri.splitQueryString)
.map((e) => _StreamInfo(e)) ??
const [];
///
late final Iterable<_StreamInfo> streams = [
...muxedStreams,
...adaptiveStreams
];
///
VideoInfoClient(this.root);
///
VideoInfoClient.parse(String raw) : root = Uri.splitQueryString(raw);
///
@alwaysThrows
static Future<VideoInfoClient> get(
YoutubeHttpClient httpClient, String videoId,
[String? sts]) {
var eurl = Uri.encodeFull('https://youtube.googleapis.com/v/$videoId');
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'
});
return retry(() async {
var raw = await httpClient.getString(url);
var result = VideoInfoClient.parse(raw);
if (!result.isVideoAvailable || !result.playerResponse.isVideoAvailable) {
throw VideoUnplayableException(videoId);
}
return result;
});
}
}
class _StreamInfo extends StreamInfoProvider {
final Map<String, String> root;
@override
late final int tag = int.parse(root['itag']!);
@override
late final String url = root['url']!;
@override
late final String? signature = root['s'];
@override
late final String? signatureParameter = root['sp'];
@override
late final int? contentLength = int.tryParse(root['clen'] ??
StreamInfoProvider.contentLenExp.firstMatch(url)?.group(1) ??
'');
@override
late final int? bitrate = int.tryParse(root['bitrate'] ?? '');
late final MediaType mimeType = MediaType.parse(root['type']!);
@override
late final String container = mimeType.subtype;
late final List<String> codecs = mimeType.parameters['codecs']!
.split(',')
.map((e) => e.trim())
.toList()
.cast<String>();
@override
late final String audioCodec = codecs.last;
@override
late final String? videoCodec = isAudioOnly ? null : codecs.first;
late final bool isAudioOnly = mimeType.type == 'audio';
@override
late final String? videoQualityLabel = root['quality_label'];
late final List<int>? _size = root
.getT<String>('size')
?.split(',')
.map((e) => int.tryParse(e))
.toList()
.cast<int>();
@override
late final int? videoWidth = _size?.first;
@override
late final int? videoHeight = _size?.last;
@override
late final int? framerate = int.tryParse(root['fps'] ?? '');
_StreamInfo(this.root);
}

View File

@ -47,6 +47,9 @@ class _StreamInfo extends StreamInfoProvider {
_StreamInfo(this.root); _StreamInfo(this.root);
@override
StreamSource get source => StreamSource.dash;
@override @override
late final int tag = int.parse(root.getAttribute('id')!); late final int tag = int.parse(root.getAttribute('id')!);

View File

@ -1,8 +1,13 @@
enum StreamSource { muxed, adaptive, dash }
/// ///
abstract class StreamInfoProvider { abstract class StreamInfoProvider {
/// ///
static final RegExp contentLenExp = RegExp(r'clen=(\d+)'); static final RegExp contentLenExp = RegExp(r'clen=(\d+)');
///
StreamSource get source;
/// ///
int get tag; int get tag;

View File

@ -96,7 +96,7 @@ class PlayerResponse {
late final List<StreamInfoProvider> muxedStreams = root late final List<StreamInfoProvider> muxedStreams = root
.get('streamingData') .get('streamingData')
?.getList('formats') ?.getList('formats')
?.map((e) => _StreamInfo(e)) ?.map((e) => _StreamInfo(e, StreamSource.muxed))
.cast<StreamInfoProvider>() .cast<StreamInfoProvider>()
.toList() ?? .toList() ??
const <StreamInfoProvider>[]; const <StreamInfoProvider>[];
@ -105,7 +105,7 @@ class PlayerResponse {
late final List<StreamInfoProvider> adaptiveStreams = root late final List<StreamInfoProvider> adaptiveStreams = root
.get('streamingData') .get('streamingData')
?.getList('adaptiveFormats') ?.getList('adaptiveFormats')
?.map((e) => _StreamInfo(e)) ?.map((e) => _StreamInfo(e, StreamSource.adaptive))
.cast<StreamInfoProvider>() .cast<StreamInfoProvider>()
.toList() ?? .toList() ??
const []; const [];
@ -238,5 +238,8 @@ class _StreamInfo extends StreamInfoProvider {
return codecs.last; return codecs.last;
} }
_StreamInfo(this.root); @override
final StreamSource source;
_StreamInfo(this.root, this.source);
} }

View File

@ -156,9 +156,9 @@ class StreamsClient {
await _httpClient.getContentLength(url, validate: false) ?? await _httpClient.getContentLength(url, validate: false) ??
0; 0;
if (contentLength <= 0) { // if (contentLength <= 0) {
continue; // continue;
} // }
// Common // Common
var container = StreamContainer.parse(streamInfo.container!); var container = StreamContainer.parse(streamInfo.container!);
@ -184,7 +184,8 @@ class StreamsClient {
: videoQuality.toVideoResolution(); : videoQuality.toVideoResolution();
// Muxed // Muxed
if (!audioCodec.isNullOrWhiteSpace) { if (!audioCodec.isNullOrWhiteSpace &&
streamInfo.source != StreamSource.adaptive) {
streams[tag] = MuxedStreamInfo( streams[tag] = MuxedStreamInfo(
tag, tag,
url, url,

View File

@ -1,6 +1,6 @@
name: youtube_explode_dart 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. description: A port in dart of the youtube explode library. Supports several API functions without the need of Youtube API Key.
version: 1.10.6 version: 1.10.7-dev.1
homepage: https://github.com/Hexer10/youtube_explode_dart homepage: https://github.com/Hexer10/youtube_explode_dart