From 214a5f18033cb72e8da439e858533ba2f3ca4634 Mon Sep 17 00:00:00 2001 From: Mattia Date: Wed, 1 Sep 2021 10:43:21 +0200 Subject: [PATCH] Ignore content-length; Workaround for #162 --- .../clients/embedded_player_client.dart | 10 +- .../clients/video_info_client.dart | 150 ------------------ .../reverse_engineering/dash_manifest.dart | 3 + .../models/stream_info_provider.dart | 5 + .../player/player_response.dart | 9 +- lib/src/videos/streams/streams_client.dart | 9 +- pubspec.yaml | 2 +- 7 files changed, 27 insertions(+), 161 deletions(-) delete mode 100644 lib/src/reverse_engineering/clients/video_info_client.dart diff --git a/lib/src/reverse_engineering/clients/embedded_player_client.dart b/lib/src/reverse_engineering/clients/embedded_player_client.dart index 8c9055c..1c13d24 100644 --- a/lib/src/reverse_engineering/clients/embedded_player_client.dart +++ b/lib/src/reverse_engineering/clients/embedded_player_client.dart @@ -26,14 +26,14 @@ class EmbeddedPlayerClient { late final Iterable<_StreamInfo> muxedStreams = root .get('streamingData') ?.getList('formats') - ?.map((e) => _StreamInfo(e)) ?? + ?.map((e) => _StreamInfo(e, StreamSource.muxed)) ?? const []; /// late final Iterable<_StreamInfo> adaptiveStreams = root .get('streamingData') ?.getList('adaptiveFormats') - ?.map((e) => _StreamInfo(e)) ?? + ?.map((e) => _StreamInfo(e, StreamSource.adaptive)) ?? const []; /// @@ -131,5 +131,9 @@ class _StreamInfo extends StreamInfoProvider { @override late final int? framerate = root['fps'] ?? 0; - _StreamInfo(this.root); + + @override + final StreamSource source; + + _StreamInfo(this.root, this.source); } diff --git a/lib/src/reverse_engineering/clients/video_info_client.dart b/lib/src/reverse_engineering/clients/video_info_client.dart deleted file mode 100644 index 2c5047b..0000000 --- a/lib/src/reverse_engineering/clients/video_info_client.dart +++ /dev/null @@ -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 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 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 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 codecs = mimeType.parameters['codecs']! - .split(',') - .map((e) => e.trim()) - .toList() - .cast(); - - @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? _size = root - .getT('size') - ?.split(',') - .map((e) => int.tryParse(e)) - .toList() - .cast(); - - @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); -} diff --git a/lib/src/reverse_engineering/dash_manifest.dart b/lib/src/reverse_engineering/dash_manifest.dart index ce0c68b..8ab517c 100644 --- a/lib/src/reverse_engineering/dash_manifest.dart +++ b/lib/src/reverse_engineering/dash_manifest.dart @@ -47,6 +47,9 @@ class _StreamInfo extends StreamInfoProvider { _StreamInfo(this.root); + @override + StreamSource get source => StreamSource.dash; + @override late final int tag = int.parse(root.getAttribute('id')!); diff --git a/lib/src/reverse_engineering/models/stream_info_provider.dart b/lib/src/reverse_engineering/models/stream_info_provider.dart index 9ccb840..ff6b914 100644 --- a/lib/src/reverse_engineering/models/stream_info_provider.dart +++ b/lib/src/reverse_engineering/models/stream_info_provider.dart @@ -1,8 +1,13 @@ +enum StreamSource { muxed, adaptive, dash } + /// abstract class StreamInfoProvider { /// static final RegExp contentLenExp = RegExp(r'clen=(\d+)'); + /// + StreamSource get source; + /// int get tag; diff --git a/lib/src/reverse_engineering/player/player_response.dart b/lib/src/reverse_engineering/player/player_response.dart index c135dee..3e2f4f7 100644 --- a/lib/src/reverse_engineering/player/player_response.dart +++ b/lib/src/reverse_engineering/player/player_response.dart @@ -96,7 +96,7 @@ class PlayerResponse { late final List muxedStreams = root .get('streamingData') ?.getList('formats') - ?.map((e) => _StreamInfo(e)) + ?.map((e) => _StreamInfo(e, StreamSource.muxed)) .cast() .toList() ?? const []; @@ -105,7 +105,7 @@ class PlayerResponse { late final List adaptiveStreams = root .get('streamingData') ?.getList('adaptiveFormats') - ?.map((e) => _StreamInfo(e)) + ?.map((e) => _StreamInfo(e, StreamSource.adaptive)) .cast() .toList() ?? const []; @@ -238,5 +238,8 @@ class _StreamInfo extends StreamInfoProvider { return codecs.last; } - _StreamInfo(this.root); + @override + final StreamSource source; + + _StreamInfo(this.root, this.source); } diff --git a/lib/src/videos/streams/streams_client.dart b/lib/src/videos/streams/streams_client.dart index eb17f23..a21a1b5 100644 --- a/lib/src/videos/streams/streams_client.dart +++ b/lib/src/videos/streams/streams_client.dart @@ -156,9 +156,9 @@ class StreamsClient { await _httpClient.getContentLength(url, validate: false) ?? 0; - if (contentLength <= 0) { - continue; - } + // if (contentLength <= 0) { + // continue; + // } // Common var container = StreamContainer.parse(streamInfo.container!); @@ -184,7 +184,8 @@ class StreamsClient { : videoQuality.toVideoResolution(); // Muxed - if (!audioCodec.isNullOrWhiteSpace) { + if (!audioCodec.isNullOrWhiteSpace && + streamInfo.source != StreamSource.adaptive) { streams[tag] = MuxedStreamInfo( tag, url, diff --git a/pubspec.yaml b/pubspec.yaml index 1253845..674b578 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ 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. -version: 1.10.6 +version: 1.10.7-dev.1 homepage: https://github.com/Hexer10/youtube_explode_dart