コード例 #1
0
        private IDictionary <string, string> GetVideoInfoAsync(string videoId, string sts = "")
        {
            // Get video info
            var raw       = GetVideoInfoRawAsync(videoId, "embedded", sts); //.ConfigureAwait(false);
            var videoInfo = UrlEx.SplitQuery(raw);

            // If can't be embedded - try another value of el
            if (videoInfo.ContainsKey("errorcode"))
            {
                var errorReason = videoInfo["reason"];
                if (errorReason.Contains("&feature=player_embedded"))
                {
                    raw       = GetVideoInfoRawAsync(videoId, "detailpage", sts); //.ConfigureAwait(false);
                    videoInfo = UrlEx.SplitQuery(raw);
                }
            }

            // Check error
            if (videoInfo.ContainsKey("errorcode"))
            {
                var errorCode   = videoInfo["errorcode"].ParseInt();
                var errorReason = videoInfo["reason"];

                //throw new VideoUnavailableException(videoId, errorCode, errorReason);
                return(null);
            }

            return(videoInfo);
        }
コード例 #2
0
        private async Task <IReadOnlyDictionary <string, string> > GetVideoInfoAsync(string videoId, string sts = "")
        {
            // Get video info with 'el=embedded'
            var raw = await GetVideoInfoRawAsync(videoId, "embedded", sts).ConfigureAwait(false);

            var videoInfo = UrlEx.SplitQuery(raw);

            // If there is no error - return
            if (!videoInfo.ContainsKey("errorcode"))
            {
                return(videoInfo);
            }

            // Get video info with 'el=detailpage'
            raw = await GetVideoInfoRawAsync(videoId, "detailpage", sts).ConfigureAwait(false);

            videoInfo = UrlEx.SplitQuery(raw);

            // If there is no error - return
            if (!videoInfo.ContainsKey("errorcode"))
            {
                return(videoInfo);
            }

            // If there is error - throw
            var errorCode   = videoInfo["errorcode"].ParseInt();
            var errorReason = videoInfo["reason"];

            throw new VideoUnavailableException(videoId, errorCode, errorReason);
        }
コード例 #3
0
        public string ParseDescription()
        {
            var buffer = new StringBuilder();

            var descriptionNode = _root.QuerySelector("p#eow-description");
            var childNodes      = descriptionNode.ChildNodes;

            foreach (var childNode in childNodes)
            {
                // If it's a text node - display text content
                if (childNode.NodeType == NodeType.Text)
                {
                    buffer.Append(childNode.TextContent);
                }
                // If it's an anchor node - perform some special transformation
                else if (childNode is IHtmlAnchorElement anchorNode)
                {
                    // If the link appears shortened - get full link
                    if (anchorNode.TextContent.EndsWith("...", StringComparison.OrdinalIgnoreCase))
                    {
                        // Get href
                        var href = anchorNode.GetAttribute("href");

                        // If it's a relative link that goes through YouTube redirect - extract the actual link
                        if (href.StartsWith("/redirect", StringComparison.OrdinalIgnoreCase))
                        {
                            // Get query parameters
                            var queryParams = UrlEx.SplitQuery(anchorNode.Search);

                            // Get the actual href
                            href = queryParams["q"];
                        }
                        // If it's a relative link - prepend YouTube's host
                        else if (href.StartsWith("/", StringComparison.OrdinalIgnoreCase))
                        {
                            // Prepend host to the link to make it absolute
                            href = "https://youtube.com" + anchorNode.GetAttribute("href");
                        }

                        buffer.Append(href);
                    }
                    // Otherwise - just use its inner text
                    else
                    {
                        buffer.Append(anchorNode.TextContent);
                    }
                }
                // If it's a break row node - append new line
                else if (childNode is IHtmlBreakRowElement)
                {
                    buffer.AppendLine();
                }
            }

            return(buffer.ToString());
        }
コード例 #4
0
        private async Task <IReadOnlyDictionary <string, string> > GetVideoInfoAsync(string videoId, string sts = "")
        {
            var raw = await GetVideoInfoRawAsync(videoId, "embedded", sts).ConfigureAwait(false);

            var videoInfo = UrlEx.SplitQuery(raw);

            // Check if there is an error
            if (videoInfo.ContainsKey("errorcode"))
            {
                var errorCode   = videoInfo["errorcode"].ParseInt();
                var errorReason = videoInfo["reason"];

                throw new VideoUnavailableException(videoId, errorCode, errorReason);
            }

            return(videoInfo);
        }
コード例 #5
0
        public string ParseDescription()
        {
            var buffer = new StringBuilder();

            var descriptionNode = _root.QuerySelector("p#eow-description");
            var childNodes      = descriptionNode.ChildNodes;

            foreach (var childNode in childNodes)
            {
                if (childNode.NodeType == NodeType.Text)
                {
                    buffer.Append(childNode.TextContent);
                }
                else if (childNode is IHtmlAnchorElement anchorNode)
                {
                    // If it uses YouTube redirect - get the actual link
                    if (anchorNode.PathName.Equals("/redirect", StringComparison.OrdinalIgnoreCase))
                    {
                        // Get query parameters
                        var queryParams = UrlEx.SplitQuery(anchorNode.Search);

                        // Get the actual href
                        var actualHref = queryParams["q"].UrlDecode();

                        buffer.Append(actualHref);
                    }
                    else
                    {
                        buffer.Append(anchorNode.TextContent);
                    }
                }
                else if (childNode is IHtmlBreakRowElement)
                {
                    buffer.AppendLine();
                }
            }

            return(buffer.ToString());
        }
コード例 #6
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));
        }
コード例 #7
0
        private async Task <IReadOnlyDictionary <string, string> > GetVideoInfoAsync(string videoId, string el, string sts)
        {
            var raw = await GetVideoInfoRawAsync(videoId, el, sts).ConfigureAwait(false);

            return(UrlEx.SplitQuery(raw));
        }