Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        /// <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));
        }