Ignore content-length; Workaround for #162
This commit is contained in:
parent
994ad62cc4
commit
214a5f1803
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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')!);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue