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);
        }
예제 #2
0
        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
     );
예제 #4
0
        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);
        }
예제 #5
0
        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);
        }
예제 #7
0
        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));
            }
        }
예제 #8
0
        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);
        }
예제 #9
0
        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.");
        }
예제 #10
0
        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;
        }
예제 #11
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("");
     }
 }
예제 #14
0
        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);
            }
        }
예제 #15
0
        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. ");
            }
        }
예제 #16
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));
            }

            // 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));
        }
예제 #17
0
 public DownloadOption(string format, MuxedStreamInfo muxedStreamInfo)
     : this(format, muxedStreamInfo.VideoQualityLabel, new[] { muxedStreamInfo })
 {
 }
예제 #18
0
 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);
            }
        }
예제 #20
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));
        }
예제 #21
0
        /// <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));
        }
예제 #22
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 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));
        }
예제 #23
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 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));
        }
예제 #24
0
    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.");
            }
        }
    }