private async Task <StreamManifest> GetManifestAsync(StreamContext streamContext) { // To make sure there are no duplicates streams, group them by tag var streams = new Dictionary <int, IStreamInfo>(); foreach (var streamInfo in streamContext.StreamInfoProviders) { var tag = streamInfo.GetTag(); var url = streamInfo.GetUrl(); // Signature var signature = streamInfo.TryGetSignature(); var signatureParameter = streamInfo.TryGetSignatureParameter() ?? "signature"; if (!string.IsNullOrWhiteSpace(signature)) { signature = streamContext.CipherOperations.Decipher(signature); url = Url.SetQueryParameter(url, signatureParameter, signature); } // Content length var contentLength = streamInfo.TryGetContentLength() ?? await _httpClient.TryGetContentLengthAsync(url, false) ?? 0; if (contentLength <= 0) { continue; // broken stream URL? } // Common var container = Container.Parse(streamInfo.GetContainer()); var fileSize = new FileSize(contentLength); var bitrate = new Bitrate(streamInfo.GetBitrate()); var audioCodec = streamInfo.TryGetAudioCodec(); var videoCodec = streamInfo.TryGetVideoCodec(); // Muxed or Video-only if (!string.IsNullOrWhiteSpace(videoCodec)) { var framerate = new Framerate(streamInfo.TryGetFramerate() ?? 24); var videoQualityLabel = streamInfo.TryGetVideoQualityLabel() ?? Heuristics.GetVideoQualityLabel(tag, framerate.FramesPerSecond); var videoQuality = Heuristics.GetVideoQuality(videoQualityLabel); var videoWidth = streamInfo.TryGetVideoWidth(); var videoHeight = streamInfo.TryGetVideoHeight(); var videoResolution = videoWidth != null && videoHeight != null ? new VideoResolution(videoWidth.Value, videoHeight.Value) : Heuristics.GetVideoResolution(videoQuality); // Muxed if (!string.IsNullOrWhiteSpace(audioCodec)) { streams[tag] = new MuxedStreamInfo( tag, url, container, fileSize, bitrate, audioCodec, videoCodec, videoQualityLabel, videoQuality, videoResolution, framerate ); } // Video-only else { streams[tag] = new VideoOnlyStreamInfo( tag, url, container, fileSize, bitrate, videoCodec, videoQualityLabel, videoQuality, videoResolution, framerate ); } } // Audio-only else if (!string.IsNullOrWhiteSpace(audioCodec)) { streams[tag] = new AudioOnlyStreamInfo( tag, url, container, fileSize, bitrate, audioCodec ); } else { #if DEBUG throw FatalFailureException.Generic("Stream info doesn't contain audio/video codec information."); #endif } } return(new StreamManifest(streams.Values.ToArray())); }
private async ValueTask PopulateStreamInfosAsync( ICollection <IStreamInfo> streamInfos, IEnumerable <IStreamInfoExtractor> streamInfoExtractors, SignatureScrambler signatureScrambler, CancellationToken cancellationToken = default) { foreach (var streamInfoExtractor in streamInfoExtractors) { var itag = streamInfoExtractor.TryGetItag() ?? throw new YoutubeExplodeException("Could not extract stream itag."); var urlRaw = streamInfoExtractor.TryGetUrl() ?? throw new YoutubeExplodeException("Could not extract stream URL."); // Unscramble URL var signature = streamInfoExtractor.TryGetSignature(); var signatureParameter = streamInfoExtractor.TryGetSignatureParameter(); var url = UnscrambleStreamUrl(signatureScrambler, urlRaw, signature, signatureParameter); // Get content length var contentLength = streamInfoExtractor.TryGetContentLength() ?? await _httpClient.TryGetContentLengthAsync(url, false, cancellationToken) ?? 0; if (contentLength <= 0) { continue; // broken stream URL? } var fileSize = new FileSize(contentLength); var container = streamInfoExtractor.TryGetContainer()?.Pipe(s => new Container(s)) ?? throw new YoutubeExplodeException("Could not extract stream container."); var bitrate = streamInfoExtractor.TryGetBitrate()?.Pipe(s => new Bitrate(s)) ?? throw new YoutubeExplodeException("Could not extract stream bitrate."); var audioCodec = streamInfoExtractor.TryGetAudioCodec(); var videoCodec = streamInfoExtractor.TryGetVideoCodec(); // Muxed or video-only stream if (!string.IsNullOrWhiteSpace(videoCodec)) { var framerate = streamInfoExtractor.TryGetFramerate() ?? 24; var videoQualityLabel = streamInfoExtractor.TryGetVideoQualityLabel(); var videoQuality = !string.IsNullOrWhiteSpace(videoQualityLabel) ? VideoQuality.FromLabel(videoQualityLabel, framerate) : VideoQuality.FromItag(itag, framerate); var videoWidth = streamInfoExtractor.TryGetVideoWidth(); var videoHeight = streamInfoExtractor.TryGetVideoHeight(); var videoResolution = videoWidth is not null && videoHeight is not null ? new Resolution(videoWidth.Value, videoHeight.Value) : videoQuality.GetDefaultVideoResolution(); // Muxed if (!string.IsNullOrWhiteSpace(audioCodec)) { var streamInfo = new MuxedStreamInfo( url, container, fileSize, bitrate, audioCodec, videoCodec, videoQuality, videoResolution ); streamInfos.Add(streamInfo); } // Video-only else { var streamInfo = new VideoOnlyStreamInfo( url, container, fileSize, bitrate, videoCodec, videoQuality, videoResolution ); streamInfos.Add(streamInfo); } } // Audio-only else if (!string.IsNullOrWhiteSpace(audioCodec)) { var streamInfo = new AudioOnlyStreamInfo( url, container, fileSize, bitrate, audioCodec ); streamInfos.Add(streamInfo); } else { Debug.Fail("Stream doesn't contain neither audio nor video codec information."); } } }