public static async Task DownloadYouTubeVideoAsync(string youTubeVideoId, string destinationFolder, CancellationToken token) { YoutubeClient client = new YoutubeClient(); var videoInfo = await client.GetVideoAsync(youTubeVideoId); var streamInfoSet = await client.GetVideoMediaStreamInfosAsync(youTubeVideoId); MuxedStreamInfo streamInfo = null; if (Settings.Instance.PreferredQuality != "Highest") { streamInfo = streamInfoSet.Muxed.Where(p => p.VideoQualityLabel == Settings.Instance.PreferredQuality).FirstOrDefault(); } if (Settings.Instance.PreferredQuality == "Highest" || streamInfo == null) { streamInfo = streamInfoSet.Muxed.WithHighestVideoQuality(); } string fileExtension = streamInfo.Container.GetFileExtension(); string fileName = "[" + videoInfo.Author + "] " + videoInfo.Title + "." + fileExtension; //Remove invalid characters from filename string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch))); fileName = r.Replace(fileName, ""); await client.DownloadMediaStreamAsync(streamInfo, Path.Combine(destinationFolder, fileName), cancellationToken : token); }
public static string DownloadVideo(MuxedStreamInfo mediaStreamInfos, string name, MuxedStreamInfo info, Video v, int id, DownloadHeader header, bool isNotification = false) { string rootPath = App.GetDownloadPath("", "/YouTube"); if (rootPath.EndsWith("\\")) { rootPath = rootPath[0..^ 1];
private static VideoStreamInfo MapStreamInfo(MuxedStreamInfo muxedStreamInfo) => new VideoStreamInfo( streamUrl: muxedStreamInfo.Url, quality: (Models.VideoQuality)muxedStreamInfo.VideoQuality, qualityLabel: muxedStreamInfo.VideoQualityLabel, height: muxedStreamInfo.Resolution.Height, width: muxedStreamInfo.Resolution.Width );
public static string GetVideoDirectURL(string urlstring) { var id = YoutubeClient.ParseVideoId(urlstring); var client = new YoutubeClient(); var _streamInfoSet = client.GetVideoMediaStreamInfosAsync(id.ToString()); MuxedStreamInfo _streamInfo = _streamInfoSet.Result.Muxed.WithHighestVideoQuality(); return(_streamInfo.Url); }
private async Task ResolveMuxedStreamInfosAsync(PlayerContext context, string encodedData, ICollection <MuxedStreamInfo> streamInfos) { foreach (var streamEncoded in encodedData.Split(",")) { var streamInfoDic = UrlHelper.GetDictionaryFromUrlQuery(streamEncoded); var itag = streamInfoDic.Get("itag").ParseInt(); var url = streamInfoDic.Get("url"); var sig = streamInfoDic.GetOrDefault("s"); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetPlayerSourceAsync(context.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlHelper.SetUrlQueryParameter(url, "signature", sig); } // Probe stream and get content length long contentLength; using (var request = new HttpRequestMessage(HttpMethod.Head, url)) using (var response = await _httpService.PerformRequestAsync(request).ConfigureAwait(false)) { // Some muxed streams can be gone if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Gone) { continue; } // Ensure success response.EnsureSuccessStatusCode(); // Extract content length contentLength = response.Content.Headers.ContentLength ?? throw new ParseException("Could not extract content length"); } // Set rate bypass url = UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes"); var streamInfo = new MuxedStreamInfo(itag, url, contentLength); streamInfos.Add(streamInfo); } }
public static VideoEncoding GetVideoEncoding(MediaStreamInfo stream) { VideoStreamInfo VInfo = stream as VideoStreamInfo; MuxedStreamInfo MInfo = stream as MuxedStreamInfo; if (VInfo == null && MInfo == null) { return(VideoEncoding.H264); } return(VInfo?.VideoEncoding ?? MInfo.VideoEncoding); }
public static async Task <Stream> GetVideoClip(MuxedStreamInfo info, TimeSpan duration, ClosedCaption caption) { var secondLength = info.Size / (duration.TotalSeconds); var startRange = (caption.Offset.TotalSeconds * secondLength) + 1; var endRange = ((caption.Offset.TotalSeconds + caption.Duration.TotalSeconds) * secondLength) + 1; var testCurl = $"curl \"{info.Url}\" -H \"Range: bytes={(long)startRange}-{(long)endRange}\" -o ten_secs.mp4"; using (var client = new HttpClient()) { client.DefaultRequestHeaders.Range = new System.Net.Http.Headers.RangeHeaderValue((long)startRange, (long)endRange); return(await client.GetStreamAsync(info.Url)); } }
public static string DownloadVideo(MuxedStreamInfo info, Video v, int id) { ImageService.Instance.LoadUrl(v.Thumbnails.HighResUrl, TimeSpan.FromDays(30)); // CACHE IMAGE string fileUrl = DownloadHandleIntent(MovieType.YouTube, -1, -1, v.Title, v.Author, id, new List <BasicMirrorInfo>() { new BasicMirrorInfo() { name = info.VideoQualityLabel, mirror = info.Url } }, true, true, false, v.Thumbnails.HighResUrl, "{name}\n"); //isMovie ? "{name}\n" : ($"S{season}:E{episode} - " + "{name}\n")); return(fileUrl); }
private async Task DownloadVideoAsync(YoutubeClient youTube, MuxedStreamInfo streamInfo, YoutubeVideoCache cache) { Utils.CreateDirectory(cache.Directory); var path = Path.Combine(cache.Directory, FileNameFormatter(cache, streamInfo)); if (File.Exists(path)) { return; } Console.WriteLine($"{Code}: start download {path}"); await youTube.Videos.Streams.DownloadAsync(streamInfo, path); Console.WriteLine($"{Code}: download {path} done."); }
public FileDownload(YoutubeClient client, MuxedStreamInfo quality, string destination, IProgress <double> progress = null) { this.allowedToRun = true; this.youtubeClient = client; this.videoQuality = quality; this.sourceStream = client.GetMediaStreamAsync(quality).Result; this.destination = destination; this.disposeOnCompletion = true; this.chunkSize = 10000; this.contentLength = new Lazy <int>(() => Convert.ToInt32(GetContentLength())); this.progress = progress; this.BytesWritten = 0; }
public override bool Download(int SelectedIndex) { if (SelectedIndex == -1) { SelectedIndex = 0; } if (FileCacheDictionary.ContainsId(Id)) { Console.WriteLine("Already Exists!"); return(false); } else { WebClient client = new WebClient(); client.DownloadProgressChanged += Client_DownloadProgressChanged; client.DownloadFileCompleted += Client_DownloadFileCompleted; YoutubeClient youtubeClient = new YoutubeClient(); MediaStreamInfoSet streamInfoSet = youtubeClient.GetVideoMediaStreamInfosAsync(Id).Result; MuxedStreamInfo streamInfo = streamInfoSet.Muxed.WithHighestVideoQuality(); if (streamInfo == null) { throw new Exception("다운로드 할 수 없는 영상입니다."); } Console.WriteLine($"Audio:{streamInfo.AudioEncoding.ToString()} Quality : {streamInfo.VideoQualityLabel} Download URL : {streamInfo.Url}"); string basePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Delight\\External Sources Cache\\"; if (!Directory.Exists(basePath)) { Directory.CreateDirectory(basePath); } string path = basePath + Path.GetRandomFileName() + ".mp4"; client.DownloadFileAsync(new Uri(streamInfo.Url), path); FileCacheDictionary.AddPair(Title, path, Id); Console.WriteLine(path); return(true); } }
public static bool DownloadVideoViaYoutubeExplode(string url, string successPath, string failurePath, Action <double> progressHandler = null) { try { string id = YoutubeClient.ParseVideoId(url); YoutubeClient client = new YoutubeClient(); MediaStreamInfoSet streamInfoSet = client.GetVideoMediaStreamInfosAsync(id).Result; MuxedStreamInfo streamInfo = streamInfoSet.Muxed.First(m => m.Container.GetFileExtension().ToLower() == "mp4"); //string ext = streamInfo.Container.GetFileExtension(); client.DownloadMediaStreamAsync(streamInfo, successPath, progressHandler == null ? null : new Progress <double>(progressHandler)).Wait(); return(true); } catch (Exception exception) { Debug.WriteLine(exception); File.Create(failurePath).Close(); return(false); } }
private string GetStreamDescription(MediaStreamInfo stream) { if (stream is VideoStreamInfo) { VideoStreamInfo VStream = stream as VideoStreamInfo; return(string.Format("{0} {1}p ({2}mb)", VStream.VideoEncoding, DownloadManager.GetVideoHeight(VStream), VStream.Size / 1024 / 1024)); } else if (stream is AudioStreamInfo) { AudioStreamInfo AStream = stream as AudioStreamInfo; return(string.Format("{0} {1}kbps ({2}mb)", AStream.AudioEncoding, AStream.Bitrate / 1024, AStream.Size / 1024 / 1024)); } else if (stream is MuxedStreamInfo) { MuxedStreamInfo MStream = stream as MuxedStreamInfo; return(string.Format("{0} {1}p ({2}mb) (with audio)", MStream.VideoEncoding, DownloadManager.GetVideoHeight(MStream), MStream.Size / 1024 / 1024)); } else { return(""); } }
public static bool DownloadVideoViaYoutubeExplode(string url, string successPath, string failurePath, TaskProgressReporter reporter) { try { reporter?.ReportInDeterminate("getting links"); string id = YoutubeClient.ParseVideoId(url); YoutubeClient client = new YoutubeClient(); MediaStreamInfoSet streamInfoSet = client.GetVideoMediaStreamInfosAsync(id).Result; MuxedStreamInfo streamInfo = streamInfoSet.Muxed.First(m => m.Container.GetFileExtension().ToLower() == "mp4"); //string ext = streamInfo.Container.GetFileExtension(); client.DownloadMediaStreamAsync(streamInfo, successPath, reporter == null ? null : new Progress <double>(d => reporter.ReportDeterminate((int)(d * 100), "downloading"))).Wait(); return(true); } catch (Exception exception) { Debug.WriteLine(exception); File.Create(failurePath).Close(); return(false); } }
public async Task NEWLIBRARY_GetVideoAsync() { try { var id = YoutubeClient.ParseVideoId(Url); var client = new YoutubeClient(); var video = await client.GetVideoAsync(id.ToString()); streamInfoSet = await client.GetVideoMediaStreamInfosAsync(id.ToString()); streamInfo = streamInfoSet.Muxed.WithHighestVideoQuality(); DirectURL = streamInfo.Url; Title = video.Title; Duration = video.Duration; Resolution = new Size(streamInfo.Resolution.Width, streamInfo.Resolution.Height); ImageUrl = YoutubeGet.GetImage(Url); PrevImagesUrl = YoutubeGet.GetPrevImages(Url); } catch { MessageBox.Show("Error creating YoutubeVideoInfo object. "); } }
/// <inheritdoc /> public async Task <MediaStreamInfoSet> GetVideoMediaStreamInfosAsync(string videoId) { videoId.GuardNotNull(nameof(videoId)); if (!ValidateVideoId(videoId)) { throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId)); } // Register the time at which the request was made to calculate expiry date later on var requestedAt = DateTimeOffset.Now; // Get parser var parser = await GetPlayerResponseParserAsync(videoId, true); // Prepare stream info maps var muxedStreamInfoMap = new Dictionary <int, MuxedStreamInfo>(); var audioStreamInfoMap = new Dictionary <int, AudioStreamInfo>(); var videoStreamInfoMap = new Dictionary <int, VideoStreamInfo>(); // Parse muxed stream infos foreach (var streamInfoParser in parser.GetMuxedStreamInfos()) { // Parse info var itag = streamInfoParser.ParseItag(); var url = streamInfoParser.ParseUrl(); // Try to parse content length, otherwise get it manually var contentLength = streamInfoParser.ParseContentLength(); if (contentLength <= 0) { // Send HEAD request and get content length contentLength = await _httpClient.GetContentLengthAsync(url, false) ?? -1; // If content length is still not available - stream is gone or faulty if (contentLength <= 0) { continue; } } // Parse container var containerStr = streamInfoParser.ParseContainer(); var container = ContainerHelper.ContainerFromString(containerStr); // Parse audio encoding var audioEncodingStr = streamInfoParser.ParseAudioEncoding(); var audioEncoding = AudioEncodingHelper.AudioEncodingFromString(audioEncodingStr); // Parse video encoding var videoEncodingStr = streamInfoParser.ParseVideoEncoding(); var videoEncoding = VideoEncodingHelper.VideoEncodingFromString(videoEncodingStr); // Parse video quality label and video quality var videoQualityLabel = streamInfoParser.ParseVideoQualityLabel(); var videoQuality = VideoQualityHelper.VideoQualityFromLabel(videoQualityLabel); // Parse resolution var width = streamInfoParser.ParseWidth(); var height = streamInfoParser.ParseHeight(); var resolution = new VideoResolution(width, height); // Add stream var streamInfo = new MuxedStreamInfo(itag, url, container, contentLength, audioEncoding, videoEncoding, videoQualityLabel, videoQuality, resolution); muxedStreamInfoMap[itag] = streamInfo; } // Parse adaptive stream infos foreach (var streamInfoParser in parser.GetAdaptiveStreamInfos()) { // Parse info var itag = streamInfoParser.ParseItag(); var url = streamInfoParser.ParseUrl(); var bitrate = streamInfoParser.ParseBitrate(); // Try to parse content length, otherwise get it manually var contentLength = streamInfoParser.ParseContentLength(); if (contentLength <= 0) { // Send HEAD request and get content length contentLength = await _httpClient.GetContentLengthAsync(url, false) ?? -1; // If content length is still not available - stream is gone or faulty if (contentLength <= 0) { continue; } } // Parse container var containerStr = streamInfoParser.ParseContainer(); var container = ContainerHelper.ContainerFromString(containerStr); // If audio-only if (streamInfoParser.ParseIsAudioOnly()) { // Parse audio encoding var audioEncodingStr = streamInfoParser.ParseAudioEncoding(); var audioEncoding = AudioEncodingHelper.AudioEncodingFromString(audioEncodingStr); // Add stream var streamInfo = new AudioStreamInfo(itag, url, container, contentLength, bitrate, audioEncoding); audioStreamInfoMap[itag] = streamInfo; } // If video-only else { // Parse video encoding var videoEncodingStr = streamInfoParser.ParseVideoEncoding(); var videoEncoding = VideoEncodingHelper.VideoEncodingFromString(videoEncodingStr); // Parse video quality label and video quality var videoQualityLabel = streamInfoParser.ParseVideoQualityLabel(); var videoQuality = VideoQualityHelper.VideoQualityFromLabel(videoQualityLabel); // Parse resolution var width = streamInfoParser.ParseWidth(); var height = streamInfoParser.ParseHeight(); var resolution = new VideoResolution(width, height); // Parse framerate var framerate = streamInfoParser.ParseFramerate(); // Add stream var streamInfo = new VideoStreamInfo(itag, url, container, contentLength, bitrate, videoEncoding, videoQualityLabel, videoQuality, resolution, framerate); videoStreamInfoMap[itag] = streamInfo; } } // Parse dash manifest var dashManifestUrl = parser.ParseDashManifestUrl(); if (!dashManifestUrl.IsNullOrWhiteSpace()) { // Get the dash manifest parser var dashManifestParser = await GetDashManifestParserAsync(dashManifestUrl); // Parse dash stream infos foreach (var streamInfoParser in dashManifestParser.GetStreamInfos()) { // Parse info var itag = streamInfoParser.ParseItag(); var url = streamInfoParser.ParseUrl(); var contentLength = streamInfoParser.ParseContentLength(); var bitrate = streamInfoParser.ParseBitrate(); // Parse container var containerStr = streamInfoParser.ParseContainer(); var container = ContainerHelper.ContainerFromString(containerStr); // If audio-only if (streamInfoParser.ParseIsAudioOnly()) { // Parse audio encoding var audioEncodingStr = streamInfoParser.ParseEncoding(); var audioEncoding = AudioEncodingHelper.AudioEncodingFromString(audioEncodingStr); // Add stream var streamInfo = new AudioStreamInfo(itag, url, container, contentLength, bitrate, audioEncoding); audioStreamInfoMap[itag] = streamInfo; } // If video-only else { // Parse video encoding var videoEncodingStr = streamInfoParser.ParseEncoding(); var videoEncoding = VideoEncodingHelper.VideoEncodingFromString(videoEncodingStr); // Parse resolution var width = streamInfoParser.ParseWidth(); var height = streamInfoParser.ParseHeight(); var resolution = new VideoResolution(width, height); // Parse framerate var framerate = streamInfoParser.ParseFramerate(); // Determine video quality from height var videoQuality = VideoQualityHelper.VideoQualityFromHeight(height); // Determine video quality label from video quality and framerate var videoQualityLabel = VideoQualityHelper.VideoQualityToLabel(videoQuality, framerate); // Add stream var streamInfo = new VideoStreamInfo(itag, url, container, contentLength, bitrate, videoEncoding, videoQualityLabel, videoQuality, resolution, framerate); videoStreamInfoMap[itag] = streamInfo; } } } // Finalize stream info collections var muxedStreamInfos = muxedStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); var audioStreamInfos = audioStreamInfoMap.Values.OrderByDescending(s => s.Bitrate).ToArray(); var videoStreamInfos = videoStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); // Get the HLS manifest URL if available var hlsManifestUrl = parser.ParseHlsManifestUrl(); // Get expiry date var expiresIn = parser.ParseStreamInfoSetExpiresIn(); var validUntil = requestedAt.Add(expiresIn); return(new MediaStreamInfoSet(muxedStreamInfos, audioStreamInfos, videoStreamInfos, hlsManifestUrl, validUntil)); }
public DownloadOption(string format, MuxedStreamInfo muxedStreamInfo) : this(format, muxedStreamInfo.VideoQualityLabel, new[] { muxedStreamInfo }) { }
private static string FileNameFormatter(YoutubeVideoCache cache, MuxedStreamInfo streamInfo) => $"{cache.Hash}_{streamInfo.Resolution.Height}.{streamInfo.Container}";
public static int GetVideoHeight(MediaStreamInfo stream) { VideoStreamInfo VInfo = stream as VideoStreamInfo; MuxedStreamInfo MInfo = stream as MuxedStreamInfo; if (VInfo != null) { if (VInfo.Resolution != null) { return(VInfo.Resolution.Height); } else { return(0); } } else if (MInfo != null) { VideoQuality Q = VInfo?.VideoQuality ?? MInfo.VideoQuality; if (Q == VideoQuality.High4320) { return(4320); } else if (Q == VideoQuality.High3072) { return(3072); } else if (Q == VideoQuality.High2160) { return(2160); } else if (Q == VideoQuality.High1440) { return(1440); } else if (Q == VideoQuality.High1080) { return(1080); } else if (Q == VideoQuality.High720) { return(720); } else if (Q == VideoQuality.Medium480) { return(480); } else if (Q == VideoQuality.Medium360) { return(360); } else if (Q == VideoQuality.Low240) { return(240); } else if (Q == VideoQuality.Low144) { return(144); } else { return(0); } } else { return(0); } }
/// <inheritdoc /> public async Task <MediaStreamInfoSet> GetVideoMediaStreamInfosAsync(string videoId) { videoId.GuardNotNull(nameof(videoId)); if (!ValidateVideoId(videoId)) { throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId)); } // Get player context var playerContext = await GetVideoPlayerContextAsync(videoId).ConfigureAwait(false); // Get video info var videoInfo = await GetVideoInfoAsync(videoId, playerContext.Sts).ConfigureAwait(false); // Check if requires purchase if (videoInfo.ContainsKey("ypc_vid")) { var previewVideoId = videoInfo["ypc_vid"]; throw new VideoRequiresPurchaseException(videoId, previewVideoId); } // Prepare stream info collections var muxedStreamInfoMap = new Dictionary <int, MuxedStreamInfo>(); var audioStreamInfoMap = new Dictionary <int, AudioStreamInfo>(); var videoStreamInfoMap = new Dictionary <int, VideoStreamInfo>(); // Resolve muxed streams var muxedStreamInfosEncoded = videoInfo.GetOrDefault("url_encoded_fmt_stream_map"); if (muxedStreamInfosEncoded.IsNotBlank()) { foreach (var streamEncoded in muxedStreamInfosEncoded.Split(",")) { var streamInfoDic = UrlEx.SplitQuery(streamEncoded); // Extract values var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; var sig = streamInfoDic.GetOrDefault("s"); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlEx.SetQueryParameter(url, "signature", sig); } // Probe stream and get content length var contentLength = await _httpClient.GetContentLengthAsync(url, false).ConfigureAwait(false) ?? -1; // If probe failed, the stream is gone or faulty if (contentLength < 0) { continue; } var streamInfo = new MuxedStreamInfo(itag, url, contentLength); muxedStreamInfoMap[itag] = streamInfo; } } // Resolve adaptive streams var adaptiveStreamInfosEncoded = videoInfo.GetOrDefault("adaptive_fmts"); if (adaptiveStreamInfosEncoded.IsNotBlank()) { foreach (var streamEncoded in adaptiveStreamInfosEncoded.Split(",")) { var streamInfoDic = UrlEx.SplitQuery(streamEncoded); // Extract values var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; var sig = streamInfoDic.GetOrDefault("s"); var contentLength = streamInfoDic["clen"].ParseLong(); var bitrate = streamInfoDic["bitrate"].ParseLong(); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlEx.SetQueryParameter(url, "signature", sig); } // Check if audio var isAudio = streamInfoDic["type"].Contains("audio/"); // If audio stream if (isAudio) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfoMap[itag] = streamInfo; } // If video stream else { // Parse additional data var size = streamInfoDic["size"]; var width = size.SubstringUntil("x").ParseInt(); var height = size.SubstringAfter("x").ParseInt(); var resolution = new VideoResolution(width, height); var framerate = streamInfoDic["fps"].ParseInt(); var qualityLabel = streamInfoDic["quality_label"]; var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate, qualityLabel); videoStreamInfoMap[itag] = streamInfo; } } } // Resolve dash streams var dashManifestUrl = videoInfo.GetOrDefault("dashmpd"); if (dashManifestUrl.IsNotBlank()) { // Parse signature var sig = Regex.Match(dashManifestUrl, @"/s/(.*?)(?:/|$)").Groups[1].Value; // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); dashManifestUrl = UrlEx.SetRouteParameter(dashManifestUrl, "signature", sig); } // Get the manifest var response = await _httpClient.GetStringAsync(dashManifestUrl).ConfigureAwait(false); var dashManifestXml = XElement.Parse(response).StripNamespaces(); var streamsXml = dashManifestXml.Descendants("Representation"); // Parse streams foreach (var streamXml in streamsXml) { // Skip partial streams if (streamXml.Descendants("Initialization").FirstOrDefault()?.Attribute("sourceURL")?.Value .Contains("sq/") == true) { continue; } // Extract values var itag = (int)streamXml.Attribute("id"); var url = (string)streamXml.Element("BaseURL"); var bitrate = (long)streamXml.Attribute("bandwidth"); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Parse content length var contentLength = Regex.Match(url, @"clen[/=](\d+)").Groups[1].Value.ParseLong(); // Check if audio stream var isAudio = streamXml.Element("AudioChannelConfiguration") != null; // If audio stream if (isAudio) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfoMap[itag] = streamInfo; } // If video stream else { // Parse additional data var width = (int)streamXml.Attribute("width"); var height = (int)streamXml.Attribute("height"); var resolution = new VideoResolution(width, height); var framerate = (int)streamXml.Attribute("frameRate"); var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreamInfoMap[itag] = streamInfo; } } } // Get the raw HLS stream playlist (*.m3u8) var hlsLiveStreamUrl = videoInfo.GetOrDefault("hlsvp"); // Finalize stream info collections var muxedStreamInfos = muxedStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); var audioStreamInfos = audioStreamInfoMap.Values.OrderByDescending(s => s.Bitrate).ToArray(); var videoStreamInfos = videoStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); return(new MediaStreamInfoSet(muxedStreamInfos, audioStreamInfos, videoStreamInfos, hlsLiveStreamUrl)); }
/// <summary> /// Gets a set of all available media stream infos for given video. /// </summary> public async Task <MediaStreamInfoSet> GetVideoMediaStreamInfosAsync(string videoId) { videoId.GuardNotNull(nameof(videoId)); if (!ValidateVideoId(videoId)) { throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId)); } // Get player context var playerContext = await GetVideoPlayerContextAsync(videoId).ConfigureAwait(false); // Get video info var videoInfo = await GetVideoInfoAsync(videoId, playerContext.Sts).ConfigureAwait(false); // Check if requires purchase if (videoInfo.ContainsKey("ypc_vid")) { var previewVideoId = videoInfo["ypc_vid"]; throw new VideoRequiresPurchaseException(videoId, previewVideoId); } // Prepare stream info collections var muxedStreamInfos = new List <MuxedStreamInfo>(); var audioStreamInfos = new List <AudioStreamInfo>(); var videoStreamInfos = new List <VideoStreamInfo>(); // Resolve muxed streams var muxedStreamInfosEncoded = videoInfo.GetOrDefault("url_encoded_fmt_stream_map"); if (muxedStreamInfosEncoded.IsNotBlank()) { foreach (var streamEncoded in muxedStreamInfosEncoded.Split(",")) { var streamInfoDic = UrlHelper.SplitUrlQuery(streamEncoded); // Extract values var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; var sig = streamInfoDic.GetOrDefault("s"); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlHelper.SetUrlQueryParameter(url, "signature", sig); } // Set rate bypass url = UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes"); // Probe stream and get content length long contentLength; using (var request = new HttpRequestMessage(HttpMethod.Head, url)) using (var response = await _httpService.PerformRequestAsync(request).ConfigureAwait(false)) { // Some muxed streams can be gone if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Gone) { continue; } // Ensure success response.EnsureSuccessStatusCode(); // Extract content length contentLength = response.Content.Headers.ContentLength ?? throw new ParseException("Could not extract content length of muxed stream."); } var streamInfo = new MuxedStreamInfo(itag, url, contentLength); muxedStreamInfos.Add(streamInfo); } } // Resolve adaptive streams var adaptiveStreamInfosEncoded = videoInfo.GetOrDefault("adaptive_fmts"); if (adaptiveStreamInfosEncoded.IsNotBlank()) { foreach (var streamEncoded in adaptiveStreamInfosEncoded.Split(",")) { var streamInfoDic = UrlHelper.SplitUrlQuery(streamEncoded); // Extract values var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; var sig = streamInfoDic.GetOrDefault("s"); var contentLength = streamInfoDic["clen"].ParseLong(); var bitrate = streamInfoDic["bitrate"].ParseLong(); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlHelper.SetUrlQueryParameter(url, "signature", sig); } // Set rate bypass url = UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes"); // Check if audio var isAudio = streamInfoDic["type"].Contains("audio/"); // If audio stream if (isAudio) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfos.Add(streamInfo); } // If video stream else { // Parse additional data var size = streamInfoDic["size"]; var width = size.SubstringUntil("x").ParseInt(); var height = size.SubstringAfter("x").ParseInt(); var resolution = new VideoResolution(width, height); var framerate = streamInfoDic["fps"].ParseInt(); var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreamInfos.Add(streamInfo); } } } // Resolve dash streams var dashManifestUrl = videoInfo.GetOrDefault("dashmpd"); if (dashManifestUrl.IsNotBlank()) { // Parse signature var sig = Regex.Match(dashManifestUrl, @"/s/(.*?)(?:/|$)").Groups[1].Value; // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); sig = playerSource.Decipher(sig); dashManifestUrl = UrlHelper.SetUrlRouteParameter(dashManifestUrl, "signature", sig); } // Get the manifest var response = await _httpService.GetStringAsync(dashManifestUrl).ConfigureAwait(false); var dashManifestXml = XElement.Parse(response).StripNamespaces(); var streamsXml = dashManifestXml.Descendants("Representation"); // Parse streams foreach (var streamXml in streamsXml) { // Skip partial streams if (streamXml.Descendants("Initialization").FirstOrDefault()?.Attribute("sourceURL")?.Value .Contains("sq/") == true) { continue; } // Extract values var itag = (int)streamXml.Attribute("id"); var url = (string)streamXml.Element("BaseURL"); var bitrate = (long)streamXml.Attribute("bandwidth"); #if RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif // Parse content length var contentLength = Regex.Match(url, @"clen[/=](\d+)").Groups[1].Value.ParseLong(); // Set rate bypass url = url.Contains("?") ? UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes") : UrlHelper.SetUrlRouteParameter(url, "ratebypass", "yes"); // Check if audio stream var isAudio = streamXml.Element("AudioChannelConfiguration") != null; // If audio stream if (isAudio) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfos.Add(streamInfo); } // If video stream else { // Parse additional data var width = (int)streamXml.Attribute("width"); var height = (int)streamXml.Attribute("height"); var resolution = new VideoResolution(width, height); var framerate = (int)streamXml.Attribute("frameRate"); var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreamInfos.Add(streamInfo); } } } // Get the raw HLS stream playlist (*.m3u8) var hlsLiveStreamUrl = videoInfo.GetOrDefault("hlsvp"); // Finalize stream info collections muxedStreamInfos = muxedStreamInfos.Distinct(s => s.Itag).OrderByDescending(s => s.VideoQuality).ToList(); audioStreamInfos = audioStreamInfos.Distinct(s => s.Itag).OrderByDescending(s => s.Bitrate).ToList(); videoStreamInfos = videoStreamInfos.Distinct(s => s.Itag).OrderByDescending(s => s.VideoQuality).ToList(); return(new MediaStreamInfoSet(muxedStreamInfos, audioStreamInfos, videoStreamInfos, hlsLiveStreamUrl)); }
/// <inheritdoc /> public async Task <MediaStreamInfoSet> GetVideoMediaStreamInfosAsync(string videoId) { videoId.GuardNotNull(nameof(videoId)); if (!ValidateVideoId(videoId)) { throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId)); } // Get player context var playerContext = await GetVideoPlayerContextAsync(videoId).ConfigureAwait(false); // Get parser var parser = await GetVideoInfoParserAsync(videoId, playerContext.Sts).ConfigureAwait(false); // Check if video requires purchase var previewVideoId = parser.ParsePreviewVideoId(); if (previewVideoId.IsNotBlank()) { throw new VideoRequiresPurchaseException(videoId, previewVideoId); } // Prepare stream info maps var muxedStreamInfoMap = new Dictionary <int, MuxedStreamInfo>(); var audioStreamInfoMap = new Dictionary <int, AudioStreamInfo>(); var videoStreamInfoMap = new Dictionary <int, VideoStreamInfo>(); // Parse muxed stream infos foreach (var muxedStreamInfoParser in parser.GetMuxedStreamInfos()) { // Extract itag var itag = muxedStreamInfoParser.ParseItag(); #if RELEASE // Skip unknown itags if (!ItagHelper.IsKnown(itag)) { continue; } #endif // Extract URL var url = muxedStreamInfoParser.ParseUrl(); // Decipher signature if needed var signature = muxedStreamInfoParser.ParseSignature(); if (signature.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); signature = playerSource.Decipher(signature); url = UrlEx.SetQueryParameter(url, "signature", signature); } // Probe stream and get content length var contentLength = await _httpClient.GetContentLengthAsync(url, false).ConfigureAwait(false) ?? -1; // If probe failed or content length is 0, it means the stream is gone or faulty if (contentLength <= 0) { continue; } var streamInfo = new MuxedStreamInfo(itag, url, contentLength); muxedStreamInfoMap[itag] = streamInfo; } // Parse adaptive stream infos foreach (var adaptiveStreamInfoParser in parser.GetAdaptiveStreamInfos()) { // Extract info var itag = adaptiveStreamInfoParser.ParseItag(); #if RELEASE // Skip unknown itags if (!ItagHelper.IsKnown(itag)) { continue; } #endif // Extract content length var contentLength = adaptiveStreamInfoParser.ParseContentLength(); // If content length is 0, it means that the stream is gone or faulty if (contentLength <= 0) { continue; } // Extract URL var url = adaptiveStreamInfoParser.ParseUrl(); // Decipher signature if needed var signature = adaptiveStreamInfoParser.ParseSignature(); if (signature.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); signature = playerSource.Decipher(signature); url = UrlEx.SetQueryParameter(url, "signature", signature); } // Extract bitrate var bitrate = adaptiveStreamInfoParser.ParseBitrate(); // If audio-only if (adaptiveStreamInfoParser.ParseIsAudioOnly()) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfoMap[itag] = streamInfo; } // If video-only else { // Extract info var width = adaptiveStreamInfoParser.ParseWidth(); var height = adaptiveStreamInfoParser.ParseHeight(); var framerate = adaptiveStreamInfoParser.ParseFramerate(); var qualityLabel = adaptiveStreamInfoParser.ParseQualityLabel(); var resolution = new VideoResolution(width, height); var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate, qualityLabel); videoStreamInfoMap[itag] = streamInfo; } } // Parse dash manifest var dashManifestUrl = parser.ParseDashManifestUrl(); if (dashManifestUrl.IsNotBlank()) { // Parse signature var signature = Regex.Match(dashManifestUrl, @"/s/(.*?)(?:/|$)").Groups[1].Value; // Decipher signature if needed if (signature.IsNotBlank()) { var playerSource = await GetVideoPlayerSourceAsync(playerContext.SourceUrl).ConfigureAwait(false); signature = playerSource.Decipher(signature); dashManifestUrl = UrlEx.SetRouteParameter(dashManifestUrl, "signature", signature); } // Get the dash manifest parser var dashManifestParser = await GetDashManifestParserAsync(dashManifestUrl).ConfigureAwait(false); // Parse dash stream infos foreach (var dashStreamInfoParser in dashManifestParser.GetStreamInfos()) { // Extract itag var itag = dashStreamInfoParser.ParseItag(); #if RELEASE // Skip unknown itags if (!ItagHelper.IsKnown(itag)) { continue; } #endif // Extract info var url = dashStreamInfoParser.ParseUrl(); var contentLength = dashStreamInfoParser.ParseContentLength(); var bitrate = dashStreamInfoParser.ParseBitrate(); // If audio-only if (dashStreamInfoParser.ParseIsAudioOnly()) { var streamInfo = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreamInfoMap[itag] = streamInfo; } // If video-only else { // Parse additional data var width = dashStreamInfoParser.ParseWidth(); var height = dashStreamInfoParser.ParseHeight(); var framerate = dashStreamInfoParser.ParseFramerate(); var resolution = new VideoResolution(width, height); var streamInfo = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreamInfoMap[itag] = streamInfo; } } } // Get the raw HLS stream playlist (*.m3u8) var hlsPlaylistUrl = parser.ParseHlsPlaylistUrl(); // Finalize stream info collections var muxedStreamInfos = muxedStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); var audioStreamInfos = audioStreamInfoMap.Values.OrderByDescending(s => s.Bitrate).ToArray(); var videoStreamInfos = videoStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); return(new MediaStreamInfoSet(muxedStreamInfos, audioStreamInfos, videoStreamInfos, hlsPlaylistUrl)); }
/// <inheritdoc /> public async Task <MediaStreamInfoSet> GetVideoMediaStreamInfosAsync(string videoId) { videoId.GuardNotNull(nameof(videoId)); if (!ValidateVideoId(videoId)) { throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId)); } // Get player configuration var playerConfiguration = await GetPlayerConfigurationAsync(videoId).ConfigureAwait(false); // Prepare stream info maps var muxedStreamInfoMap = new Dictionary <int, MuxedStreamInfo>(); var audioStreamInfoMap = new Dictionary <int, AudioStreamInfo>(); var videoStreamInfoMap = new Dictionary <int, VideoStreamInfo>(); // Get muxed stream infos var muxedStreamInfoDics = playerConfiguration.MuxedStreamInfosUrlEncoded.EmptyIfNull().Split(",").Select(Url.SplitQuery); foreach (var streamInfoDic in muxedStreamInfoDics) { // Extract info var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; // Decipher signature if needed var signature = streamInfoDic.GetValueOrDefault("s"); if (!signature.IsNullOrWhiteSpace()) { // Get cipher operations (cached) var cipherOperations = await GetCipherOperationsAsync(playerConfiguration.PlayerSourceUrl).ConfigureAwait(false); // Decipher signature signature = cipherOperations.Decipher(signature); // Set the corresponding parameter in the URL var signatureParameter = streamInfoDic.GetValueOrDefault("sp") ?? "signature"; url = Url.SetQueryParameter(url, signatureParameter, signature); } // Try to extract content length, otherwise get it manually var contentLength = Regex.Match(url, @"clen=(\d+)").Groups[1].Value.ParseLongOrDefault(); if (contentLength <= 0) { // Send HEAD request and get content length contentLength = await _httpClient.GetContentLengthAsync(url, false).ConfigureAwait(false) ?? 0; // If content length is still not available - stream is gone or faulty if (contentLength <= 0) { continue; } } // Extract container var containerRaw = streamInfoDic["type"].SubstringUntil(";").SubstringAfter("/"); var container = Heuristics.ContainerFromString(containerRaw); // Extract audio encoding var audioEncodingRaw = streamInfoDic["type"].SubstringAfter("codecs=\"").SubstringUntil("\"").Split(", ").Last(); var audioEncoding = Heuristics.AudioEncodingFromString(audioEncodingRaw); // Extract video encoding var videoEncodingRaw = streamInfoDic["type"].SubstringAfter("codecs=\"").SubstringUntil("\"").Split(", ").First(); var videoEncoding = Heuristics.VideoEncodingFromString(videoEncodingRaw); // Determine video quality from itag var videoQuality = Heuristics.VideoQualityFromItag(itag); // Determine video quality label from video quality var videoQualityLabel = Heuristics.VideoQualityToLabel(videoQuality); // Determine video resolution from video quality var resolution = Heuristics.VideoQualityToResolution(videoQuality); // Add to list muxedStreamInfoMap[itag] = new MuxedStreamInfo(itag, url, container, contentLength, audioEncoding, videoEncoding, videoQualityLabel, videoQuality, resolution); } // Get adaptive stream infos var adaptiveStreamInfoDics = playerConfiguration.AdaptiveStreamInfosUrlEncoded.EmptyIfNull().Split(",").Select(Url.SplitQuery); foreach (var streamInfoDic in adaptiveStreamInfoDics) { // Extract info var itag = streamInfoDic["itag"].ParseInt(); var url = streamInfoDic["url"]; var bitrate = streamInfoDic["bitrate"].ParseLong(); // Decipher signature if needed var signature = streamInfoDic.GetValueOrDefault("s"); if (!signature.IsNullOrWhiteSpace()) { // Get cipher operations (cached) var cipherOperations = await GetCipherOperationsAsync(playerConfiguration.PlayerSourceUrl).ConfigureAwait(false); // Decipher signature signature = cipherOperations.Decipher(signature); // Set the corresponding parameter in the URL var signatureParameter = streamInfoDic.GetValueOrDefault("sp") ?? "signature"; url = Url.SetQueryParameter(url, signatureParameter, signature); } // Try to extract content length, otherwise get it manually var contentLength = streamInfoDic.GetValueOrDefault("clen").ParseLongOrDefault(); if (contentLength <= 0) { // Send HEAD request and get content length contentLength = await _httpClient.GetContentLengthAsync(url, false).ConfigureAwait(false) ?? 0; // If content length is still not available - stream is gone or faulty if (contentLength <= 0) { continue; } } // Extract container var containerRaw = streamInfoDic["type"].SubstringUntil(";").SubstringAfter("/"); var container = Heuristics.ContainerFromString(containerRaw); // If audio-only if (streamInfoDic["type"].StartsWith("audio/", StringComparison.OrdinalIgnoreCase)) { // Extract audio encoding var audioEncodingRaw = streamInfoDic["type"].SubstringAfter("codecs=\"").SubstringUntil("\""); var audioEncoding = Heuristics.AudioEncodingFromString(audioEncodingRaw); // Add stream audioStreamInfoMap[itag] = new AudioStreamInfo(itag, url, container, contentLength, bitrate, audioEncoding); } // If video-only else { // Extract video encoding var videoEncodingRaw = streamInfoDic["type"].SubstringAfter("codecs=\"").SubstringUntil("\""); var videoEncoding = !videoEncodingRaw.Equals("unknown", StringComparison.OrdinalIgnoreCase) ? Heuristics.VideoEncodingFromString(videoEncodingRaw) : VideoEncoding.Av1; // HACK: issue 246 // Extract video quality label and video quality var videoQualityLabel = streamInfoDic["quality_label"]; var videoQuality = Heuristics.VideoQualityFromLabel(videoQualityLabel); // Extract resolution var width = streamInfoDic["size"].SubstringUntil("x").ParseInt(); var height = streamInfoDic["size"].SubstringAfter("x").ParseInt(); var resolution = new VideoResolution(width, height); // Extract framerate var framerate = streamInfoDic["fps"].ParseInt(); // Add to list videoStreamInfoMap[itag] = new VideoStreamInfo(itag, url, container, contentLength, bitrate, videoEncoding, videoQualityLabel, videoQuality, resolution, framerate); } } // Get dash manifest var dashManifestUrl = playerConfiguration.DashManifestUrl; if (!dashManifestUrl.IsNullOrWhiteSpace()) { // Extract signature var signature = Regex.Match(dashManifestUrl, "/s/(.*?)(?:/|$)").Groups[1].Value; // Decipher signature if needed if (!signature.IsNullOrWhiteSpace()) { // Get cipher operations (cached) var cipherOperations = await GetCipherOperationsAsync(playerConfiguration.PlayerSourceUrl).ConfigureAwait(false); // Decipher signature signature = cipherOperations.Decipher(signature); // Set the corresponding parameter in the URL dashManifestUrl = Url.SetRouteParameter(dashManifestUrl, "signature", signature); } // Get DASH manifest XML var dashManifestXml = await GetDashManifestXmlAsync(dashManifestUrl).ConfigureAwait(false); // Get representation nodes (skip partial streams) var streamInfoXmls = dashManifestXml.Descendants("Representation").Where(s => s.Descendants("Initialization").FirstOrDefault()?.Attribute("sourceURL")?.Value.Contains("sq/") != true); // Get DASH stream infos foreach (var streamInfoXml in streamInfoXmls) { // Extract info var itag = (int)streamInfoXml.Attribute("id"); var url = (string)streamInfoXml.Element("BaseURL"); var contentLength = Regex.Match(url, @"clen[/=](\d+)").Groups[1].Value.ParseLong(); var bitrate = (long)streamInfoXml.Attribute("bandwidth"); // Extract container var containerRaw = Regex.Match(url, @"mime[/=]\w*%2F([\w\d]*)").Groups[1].Value.UrlDecode(); var container = Heuristics.ContainerFromString(containerRaw); // If audio-only if (streamInfoXml.Element("AudioChannelConfiguration") != null) { // Extract audio encoding var audioEncodingRaw = (string)streamInfoXml.Attribute("codecs"); var audioEncoding = Heuristics.AudioEncodingFromString(audioEncodingRaw); // Add to list audioStreamInfoMap[itag] = new AudioStreamInfo(itag, url, container, contentLength, bitrate, audioEncoding); } // If video-only else { // Extract video encoding var videoEncodingRaw = (string)streamInfoXml.Attribute("codecs"); var videoEncoding = Heuristics.VideoEncodingFromString(videoEncodingRaw); // Extract resolution var width = (int)streamInfoXml.Attribute("width"); var height = (int)streamInfoXml.Attribute("height"); var resolution = new VideoResolution(width, height); // Extract framerate var framerate = (int)streamInfoXml.Attribute("frameRate"); // Determine video quality from itag var videoQuality = Heuristics.VideoQualityFromItag(itag); // Determine video quality label from video quality and framerate var videoQualityLabel = Heuristics.VideoQualityToLabel(videoQuality, framerate); // Add to list videoStreamInfoMap[itag] = new VideoStreamInfo(itag, url, container, contentLength, bitrate, videoEncoding, videoQualityLabel, videoQuality, resolution, framerate); } } } // Finalize stream info collections var muxedStreamInfos = muxedStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); var audioStreamInfos = audioStreamInfoMap.Values.OrderByDescending(s => s.Bitrate).ToArray(); var videoStreamInfos = videoStreamInfoMap.Values.OrderByDescending(s => s.VideoQuality).ToArray(); return(new MediaStreamInfoSet(muxedStreamInfos, audioStreamInfos, videoStreamInfos, playerConfiguration.HlsManifestUrl, playerConfiguration.ValidUntil)); }
private async ValueTask PopulateStreamInfosAsync( ICollection <IStreamInfo> streamInfos, IEnumerable <IStreamInfoExtractor> streamInfoExtractors, CancellationToken cancellationToken = default) { foreach (var streamInfoExtractor in streamInfoExtractors) { var itag = streamInfoExtractor.TryGetItag() ?? throw new YoutubeExplodeException("Could not extract stream itag."); var url = streamInfoExtractor.TryGetUrl() ?? throw new YoutubeExplodeException("Could not extract stream URL."); // Get content length var contentLength = streamInfoExtractor.TryGetContentLength() ?? await _http.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."); } } }