public static async Task <string> StreamUrl(Song Song) { if (Song.Type == SongType.YouTube) { YoutubeClient.TryParseVideoId(Song.Url, out string Id); var Client = new YoutubeClient(); var Vid = await Client.GetVideoInfoAsync(Id); MixedStreamInfo Max = null; foreach (var V in Vid.MixedStreams) { if (Max == null || V.VideoQuality > Max.VideoQuality) { Max = V; } } return(Max?.Url ?? string.Empty); } else if (Song.Type == SongType.SoundCloud) { var SCRes = await($"http://api.soundcloud.com/resolve?url={Song.Url}&{SC}").WebResponseRetryLoop(); if (SCRes != string.Empty && SCRes.StartsWith("{\"kind\":\"track\"")) { var Parse = JObject.Parse(SCRes); if (Parse["downloadable"] != null && (bool)Parse["downloadable"] == true) { return($"{Parse["download_url"]}?{SC}"); } return($"{Parse["stream_url"]}?{SC}"); } } else if (Song.Type == SongType.Telegram) { return(await GetTelegramUrl(Song.Url)); } return(Song.Url); }
/// <summary> /// Gets video info by ID /// </summary> public async Task <VideoInfo> GetVideoInfoAsync(string videoId) { if (videoId == null) { throw new ArgumentNullException(nameof(videoId)); } if (!ValidateVideoId(videoId)) { throw new ArgumentException("Invalid Youtube video ID", nameof(videoId)); } // Get player context var playerContext = await GetPlayerContextAsync(videoId).ConfigureAwait(false); // Get video info string request = $"https://www.youtube.com/get_video_info?video_id={videoId}&sts={playerContext.Sts}&el=info&ps=default&hl=en"; string response = await _httpService.GetStringAsync(request).ConfigureAwait(false); var videoInfoDic = UrlHelper.GetDictionaryFromUrlQuery(response); // Check for error if (videoInfoDic.ContainsKey("errorcode")) { int errorCode = videoInfoDic.Get("errorcode").ParseInt(); string errorReason = videoInfoDic.GetOrDefault("reason"); throw new VideoNotAvailableException(errorCode, errorReason); } // Check for paid content if (videoInfoDic.GetOrDefault("requires_purchase") == "1") { throw new VideoRequiresPurchaseException(); } // Parse metadata string title = videoInfoDic.Get("title"); var duration = TimeSpan.FromSeconds(videoInfoDic.Get("length_seconds").ParseDouble()); long viewCount = videoInfoDic.Get("view_count").ParseLong(); var keywords = videoInfoDic.Get("keywords").Split(","); var watermarks = videoInfoDic.Get("watermark").Split(","); bool isListed = videoInfoDic.Get("is_listed") == "1"; bool isRatingAllowed = videoInfoDic.Get("allow_ratings") == "1"; bool isMuted = videoInfoDic.Get("muted") == "1"; bool isEmbeddingAllowed = videoInfoDic.Get("allow_embed") == "1"; // Parse mixed streams var mixedStreams = new List <MixedStreamInfo>(); string mixedStreamsEncoded = videoInfoDic.GetOrDefault("url_encoded_fmt_stream_map"); if (mixedStreamsEncoded.IsNotBlank()) { foreach (string streamEncoded in mixedStreamsEncoded.Split(",")) { var streamDic = UrlHelper.GetDictionaryFromUrlQuery(streamEncoded); int itag = streamDic.Get("itag").ParseInt(); #if RELEASE // Skip unknown itags on RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif string url = streamDic.Get("url"); string sig = streamDic.GetOrDefault("s"); // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetPlayerSourceAsync(playerContext.Version).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlHelper.SetUrlQueryParameter(url, "signature", sig); } // Get content length long contentLength; using (var reqMsg = new HttpRequestMessage(HttpMethod.Head, url)) using (var resMsg = await _httpService.PerformRequestAsync(reqMsg).ConfigureAwait(false)) { // Check status code (https://github.com/Tyrrrz/YoutubeExplode/issues/36) if (resMsg.StatusCode == HttpStatusCode.NotFound || resMsg.StatusCode == HttpStatusCode.Gone) { continue; } // Ensure success resMsg.EnsureSuccessStatusCode(); // Extract content length contentLength = resMsg.Content.Headers.ContentLength ?? -1; if (contentLength < 0) { throw new ParseException("Could not extract content length"); } } // Set rate bypass url = UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes"); var stream = new MixedStreamInfo(itag, url, contentLength); mixedStreams.Add(stream); } } // Parse adaptive streams var audioStreams = new List <AudioStreamInfo>(); var videoStreams = new List <VideoStreamInfo>(); string adaptiveStreamsEncoded = videoInfoDic.GetOrDefault("adaptive_fmts"); if (adaptiveStreamsEncoded.IsNotBlank()) { foreach (string streamEncoded in adaptiveStreamsEncoded.Split(",")) { var streamDic = UrlHelper.GetDictionaryFromUrlQuery(streamEncoded); int itag = streamDic.Get("itag").ParseInt(); #if RELEASE // Skip unknown itags on RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif string url = streamDic.Get("url"); string sig = streamDic.GetOrDefault("s"); long contentLength = streamDic.Get("clen").ParseLong(); long bitrate = streamDic.Get("bitrate").ParseLong(); // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetPlayerSourceAsync(playerContext.Version).ConfigureAwait(false); sig = playerSource.Decipher(sig); url = UrlHelper.SetUrlQueryParameter(url, "signature", sig); } // Set rate bypass url = UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes"); // Check if audio bool isAudio = streamDic.Get("type").Contains("audio/"); // If audio stream if (isAudio) { var stream = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreams.Add(stream); } // If video stream else { // Parse additional data string size = streamDic.Get("size"); int width = size.SubstringUntil("x").ParseInt(); int height = size.SubstringAfter("x").ParseInt(); var resolution = new VideoResolution(width, height); double framerate = streamDic.Get("fps").ParseDouble(); var stream = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreams.Add(stream); } } } // Parse adaptive streams from dash string dashManifestUrl = videoInfoDic.GetOrDefault("dashmpd"); if (dashManifestUrl.IsNotBlank()) { // Parse signature string sig = Regex.Match(dashManifestUrl, @"/s/(.*?)(?:/|$)").Groups[1].Value; // Decipher signature if needed if (sig.IsNotBlank()) { var playerSource = await GetPlayerSourceAsync(playerContext.Version).ConfigureAwait(false); sig = playerSource.Decipher(sig); dashManifestUrl = UrlHelper.SetUrlPathParameter(dashManifestUrl, "signature", sig); } // Get the manifest response = await _httpService.GetStringAsync(dashManifestUrl).ConfigureAwait(false); var dashManifestXml = XElement.Parse(response).StripNamespaces(); var streamsXml = dashManifestXml.Descendants("Representation"); // Filter out partial streams streamsXml = streamsXml .Where(x => !(x.Descendant("Initialization") ?.Attribute("sourceURL") ?.Value.Contains("sq/") ?? false)); // Parse streams foreach (var streamXml in streamsXml) { int itag = (int)streamXml.AttributeStrict("id"); #if RELEASE // Skip unknown itags on RELEASE if (!MediaStreamInfo.IsKnown(itag)) { continue; } #endif string url = streamXml.ElementStrict("BaseURL").Value; long bitrate = (long)streamXml.AttributeStrict("bandwidth"); // Parse content length long contentLength = Regex.Match(url, @"clen[/=](\d+)").Groups[1].Value.ParseLong(); // Set rate bypass url = url.Contains("&") ? UrlHelper.SetUrlQueryParameter(url, "ratebypass", "yes") : UrlHelper.SetUrlPathParameter(url, "ratebypass", "yes"); // Check if audio stream bool isAudio = streamXml.Element("AudioChannelConfiguration") != null; // If audio stream if (isAudio) { var stream = new AudioStreamInfo(itag, url, contentLength, bitrate); audioStreams.Add(stream); } // If video stream else { // Parse additional data int width = (int)streamXml.AttributeStrict("width"); int height = (int)streamXml.AttributeStrict("height"); var resolution = new VideoResolution(width, height); double framerate = (double)streamXml.AttributeStrict("frameRate"); var stream = new VideoStreamInfo(itag, url, contentLength, bitrate, resolution, framerate); videoStreams.Add(stream); } } } // Finalize stream lists mixedStreams = mixedStreams.Distinct(s => s.Itag).OrderByDescending(s => s.VideoQuality).ToList(); audioStreams = audioStreams.Distinct(s => s.Itag).OrderByDescending(s => s.Bitrate).ToList(); videoStreams = videoStreams.Distinct(s => s.Itag).OrderByDescending(s => s.VideoQuality).ToList(); // Parse closed caption tracks var captions = new List <ClosedCaptionTrackInfo>(); string captionsEncoded = videoInfoDic.GetOrDefault("caption_tracks"); if (captionsEncoded.IsNotBlank()) { foreach (string captionEncoded in captionsEncoded.Split(",")) { var captionDic = UrlHelper.GetDictionaryFromUrlQuery(captionEncoded); string url = captionDic.Get("u"); bool isAuto = captionDic.Get("v").Contains("a."); string code = captionDic.Get("lc"); string name = captionDic.Get("n"); var language = new Language(code, name); var caption = new ClosedCaptionTrackInfo(url, language, isAuto); captions.Add(caption); } } // Get metadata extension request = $"https://www.youtube.com/get_video_metadata?video_id={videoId}"; response = await _httpService.GetStringAsync(request).ConfigureAwait(false); var videoInfoExtXml = XElement.Parse(response).StripNamespaces().ElementStrict("html_content"); // Parse string description = videoInfoExtXml.ElementStrict("video_info").ElementStrict("description").Value; long likeCount = (long)videoInfoExtXml.ElementStrict("video_info").ElementStrict("likes_count_unformatted"); long dislikeCount = (long)videoInfoExtXml.ElementStrict("video_info").ElementStrict("dislikes_count_unformatted"); // Parse author info string authorId = videoInfoExtXml.ElementStrict("user_info").ElementStrict("channel_external_id").Value; string authorName = videoInfoExtXml.ElementStrict("user_info").ElementStrict("username").Value; string authorTitle = videoInfoExtXml.ElementStrict("user_info").ElementStrict("channel_title").Value; bool authorIsPaid = videoInfoExtXml.ElementStrict("user_info").ElementStrict("channel_paid").Value == "1"; string authorLogoUrl = videoInfoExtXml.ElementStrict("user_info").ElementStrict("channel_logo_url").Value; string authorBannerUrl = videoInfoExtXml.ElementStrict("user_info").ElementStrict("channel_banner_url").Value; var author = new ChannelInfo( authorId, authorName, authorTitle, authorIsPaid, authorLogoUrl, authorBannerUrl); return(new VideoInfo( videoId, title, author, duration, description, keywords, watermarks, viewCount, likeCount, dislikeCount, isListed, isRatingAllowed, isMuted, isEmbeddingAllowed, mixedStreams, audioStreams, videoStreams, captions)); }