Beispiel #1
0
        /// <inheritdoc />
        public async Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, Stream output,
                                                          IProgress <double>?progress = null, CancellationToken cancellationToken = default)
        {
            // Get the track
            var track = await GetClosedCaptionTrackAsync(info).ConfigureAwait(false);

            // Save to file as SRT
            using var writer = new StreamWriter(output, Encoding.UTF8, 1024, true);
            for (var i = 0; i < track.Captions.Count; i++)
            {
                // Make sure cancellation was not requested
                cancellationToken.ThrowIfCancellationRequested();

                var caption = track.Captions[i];
                var buffer  = new StringBuilder();

                // Line number
                buffer.AppendLine((i + 1).ToString());

                // Time start --> time end
                buffer.Append(caption.Offset.ToString(@"hh\:mm\:ss\,fff"));
                buffer.Append(" --> ");
                buffer.Append((caption.Offset + caption.Duration).ToString(@"hh\:mm\:ss\,fff"));
                buffer.AppendLine();

                // Actual text
                buffer.AppendLine(caption.Text);

                // Write to stream
                await writer.WriteLineAsync(buffer.ToString()).ConfigureAwait(false);

                // Report progress
                progress?.Report((i + 1.0) / track.Captions.Count);
            }
        }
Beispiel #2
0
        /// <inheritdoc />
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo info)
        {
            info.GuardNotNull(nameof(info));

            // Get manifest
            var raw = await _httpClient.GetStringAsync(info.Url).ConfigureAwait(false);

            var trackXml = XElement.Parse(raw).StripNamespaces();

            // Parse captions
            var captions = new List <ClosedCaption>();

            foreach (var captionXml in trackXml.Descendants("p"))
            {
                var text = (string)captionXml;
                if (text.IsBlank())
                {
                    continue;                 // some autogenerated captions are blank
                }
                var offset   = TimeSpan.FromMilliseconds((double)captionXml.Attribute("t"));
                var duration = TimeSpan.FromMilliseconds((double)captionXml.Attribute("d"));

                var caption = new ClosedCaption(text, offset, duration);
                captions.Add(caption);
            }

            return(new ClosedCaptionTrack(info, captions));
        }
        /// <inheritdoc />
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo info)
        {
            info.GuardNotNull(nameof(info));

            // Get parser
            var parser = await GetClosedCaptionTrackAjaxParserAsync(info.Url).ConfigureAwait(false);

            // Parse captions
            var closedCaptions = new List <ClosedCaption>();

            foreach (var closedCaptionParser in parser.GetClosedCaptions())
            {
                // Extract info
                var text = closedCaptionParser.ParseText();

                // Skip caption tracks without text
                if (text.IsBlank())
                {
                    continue;
                }

                var offset   = closedCaptionParser.ParseOffset();
                var duration = closedCaptionParser.ParseDuration();

                var caption = new ClosedCaption(text, offset, duration);
                closedCaptions.Add(caption);
            }

            return(new ClosedCaptionTrack(info, closedCaptions));
        }
Beispiel #4
0
        /// <inheritdoc />
        public async Task <IReadOnlyList <ClosedCaptionTrackInfo> > GetVideoClosedCaptionTrackInfosAsync(string videoId)
        {
            videoId.GuardNotNull(nameof(videoId));

            if (!ValidateVideoId(videoId))
            {
                throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId));
            }

            // Get parser
            var parser = await GetPlayerResponseParserAsync(videoId);

            // Parse closed caption track infos
            var closedCaptionTrackInfos = new List <ClosedCaptionTrackInfo>();

            foreach (var closedCaptionTrackInfoParser in parser.GetClosedCaptionTrackInfos())
            {
                // Parse info
                var url             = closedCaptionTrackInfoParser.ParseUrl();
                var isAutoGenerated = closedCaptionTrackInfoParser.ParseIsAutoGenerated();

                // Parse language
                var code     = closedCaptionTrackInfoParser.ParseLanguageCode();
                var name     = closedCaptionTrackInfoParser.ParseLanguageName();
                var language = new Language(code, name);

                // Enforce format to the one we know how to parse
                url = UrlEx.SetQueryParameter(url, "format", "3");

                var closedCaptionTrackInfo = new ClosedCaptionTrackInfo(url, language, isAutoGenerated);
                closedCaptionTrackInfos.Add(closedCaptionTrackInfo);
            }

            return(closedCaptionTrackInfos);
        }
Beispiel #5
0
        /// <summary>
        /// Gets the actual closed caption track represented by given metadata
        /// </summary>
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo closedCaptionTrackInfo)
        {
            if (closedCaptionTrackInfo == null)
            {
                throw new ArgumentNullException(nameof(closedCaptionTrackInfo));
            }

            // Get
            string response = await _httpService.GetStringAsync(closedCaptionTrackInfo.Url).ConfigureAwait(false);

            var captionTrackXml = XElement.Parse(response).StripNamespaces();

            // Parse
            var captions = new List <ClosedCaption>();

            foreach (var captionXml in captionTrackXml.Descendants("text"))
            {
                string text     = captionXml.Value;
                var    offset   = TimeSpan.FromSeconds((double)captionXml.AttributeStrict("start"));
                var    duration = TimeSpan.FromSeconds((double)captionXml.AttributeStrict("dur"));

                var caption = new ClosedCaption(text, offset, duration);
                captions.Add(caption);
            }

            return(new ClosedCaptionTrack(closedCaptionTrackInfo, captions));
        }
Beispiel #6
0
    /// <summary>
    /// Writes the closed caption track identified by the specified metadata to the specified writer.
    /// </summary>
    /// <remarks>
    /// Closed captions are written in the SRT file format.
    /// </remarks>
    public async ValueTask WriteToAsync(
        ClosedCaptionTrackInfo trackInfo,
        TextWriter writer,
        IProgress <double>?progress         = null,
        CancellationToken cancellationToken = default)
    {
        var track = await GetAsync(trackInfo, cancellationToken);

        var buffer = new StringBuilder();

        for (var i = 0; i < track.Captions.Count; i++)
        {
            var caption = track.Captions[i];
            buffer.Clear();

            cancellationToken.ThrowIfCancellationRequested();

            // Line number
            buffer.AppendLine((i + 1).ToString());

            // Time start --> time end
            buffer.Append(caption.Offset.ToString(@"hh\:mm\:ss\,fff"));
            buffer.Append(" --> ");
            buffer.Append((caption.Offset + caption.Duration).ToString(@"hh\:mm\:ss\,fff"));
            buffer.AppendLine();

            // Actual text
            buffer.AppendLine(caption.Text);

            await writer.WriteLineAsync(buffer.ToString());

            progress?.Report((i + 1.0) / track.Captions.Count);
        }
    }
        /// <inheritdoc />
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo info)
        {
            info.GuardNotNull(nameof(info));

            // Get closed caption track XML
            var trackXml = await GetClosedCaptionTrackXmlAsync(info.Url).ConfigureAwait(false);

            // Get closed captions
            var captions = new List <ClosedCaption>();

            foreach (var captionXml in trackXml.Descendants("p"))
            {
                // Extract text
                var text = (string)captionXml;

                // Skip captions with no text
                if (text.IsNullOrWhiteSpace())
                {
                    continue;
                }

                // Extract timing info
                var offset   = TimeSpan.FromMilliseconds((double)captionXml.Attribute("t"));
                var duration = TimeSpan.FromMilliseconds((double)captionXml.Attribute("d"));

                // Add to list
                captions.Add(new ClosedCaption(text, offset, duration));
            }

            return(new ClosedCaptionTrack(info, captions));
        }
Beispiel #8
0
        /// <inheritdoc />
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo info)
        {
            info.GuardNotNull(nameof(info));

            // Get parser
            var parser = await GetClosedCaptionTrackAjaxParserAsync(info.Url);

            // Parse captions
            var closedCaptions = new List <ClosedCaption>();

            foreach (var closedCaptionParser in parser.GetClosedCaptions())
            {
                // Parse info
                var text = closedCaptionParser.ParseText();

                // Skip empty or whitespace captions
                if (text == null || text.IsWhiteSpace())
                {
                    continue;
                }

                var offset   = closedCaptionParser.ParseOffset();
                var duration = closedCaptionParser.ParseDuration();

                var caption = new ClosedCaption(text, offset, duration);
                closedCaptions.Add(caption);
            }

            return(new ClosedCaptionTrack(info, closedCaptions));
        }
 public YTCaptionsViewModel(string title, string id, ClosedCaptionTrackInfo item)
 {
     SearchCommand = new Command(async(text) => await ExecuteSearchCommand((string)text));
     trackInfo     = item;
     VideoTitle    = title;
     VideoId       = id;
 }
Beispiel #10
0
        /// <inheritdoc />
        public async Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, string filePath,
                                                          IProgress <double> progress = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            filePath.GuardNotNull(nameof(filePath));

            using (var output = File.Create(filePath))
                await DownloadClosedCaptionTrackAsync(info, output, progress, cancellationToken).ConfigureAwait(false);
        }
Beispiel #11
0
 /// <summary>
 /// Downloads the closed caption track identified by the specified metadata to the specified file.
 /// </summary>
 /// <remarks>
 /// Closed captions are written in the SRT file format.
 /// </remarks>
 public async ValueTask DownloadAsync(
     ClosedCaptionTrackInfo trackInfo,
     string filePath,
     IProgress <double>?progress         = null,
     CancellationToken cancellationToken = default)
 {
     using var writer = File.CreateText(filePath);
     await WriteToAsync(trackInfo, writer, progress, cancellationToken);
 }
Beispiel #12
0
    /// <summary>
    /// Gets the closed caption track identified by the specified metadata.
    /// </summary>
    public async ValueTask <ClosedCaptionTrack> GetAsync(
        ClosedCaptionTrackInfo trackInfo,
        CancellationToken cancellationToken = default)
    {
        var trackExtractor = await _controller.GetClosedCaptionTrackAsync(trackInfo.Url, cancellationToken);

        var captions = trackExtractor
                       .GetClosedCaptions()
                       .Select(c =>
        {
            var text = c.TryGetText();
            if (string.IsNullOrWhiteSpace(text))
            {
                return(null);
            }

            // Auto-generated captions may have invalid manifests:
            // https://github.com/Tyrrrz/YoutubeExplode/discussions/619

            if (c.TryGetOffset() is not {
            } offset)
            {
                return(null);
            }

            if (c.TryGetDuration() is not {
            } duration)
            {
                return(null);
            }

            var parts = c
                        .GetParts()
                        .Select(p =>
            {
                var partText = p.TryGetText();
                if (string.IsNullOrWhiteSpace(partText))
                {
                    return(null);
                }

                var partOffset =
                    p.TryGetOffset() ??
                    throw new YoutubeExplodeException("Could not extract caption part offset.");

                return(new ClosedCaptionPart(partText, partOffset));
            })
                        .WhereNotNull()
                        .ToArray();

            return(new ClosedCaption(text, offset, duration, parts));
        })
                       .WhereNotNull()
                       .ToArray();

        return(new ClosedCaptionTrack(captions));
    }
Beispiel #13
0
        public static ClosedCaptionTrack GetClosedCaptionTrack()
        {
            var info = new ClosedCaptionTrackInfo("test", new CultureInfo("en"), true);

            return(new ClosedCaptionTrack(info,
                                          new[]
            {
                new ClosedCaption("Hello", TimeSpan.Zero, TimeSpan.FromSeconds(1)),
                new ClosedCaption("world", TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1))
            }));
        }
        private ClosedCaptionTrack GetClosedCaptionTrack()
        {
            var info = new ClosedCaptionTrackInfo("test", new Language("en", "English (auto-generated)"), true);

            return(new ClosedCaptionTrack(info,
                                          new[]
            {
                new ClosedCaption("Hello", TimeSpan.Zero, TimeSpan.FromSeconds(1)),
                new ClosedCaption("world", TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1))
            }));
        }
Beispiel #15
0
        /// <inheritdoc />
        public List <ClosedCaptionTrackInfo> GetVideoClosedCaptionTrackInfosAsync(string videoId)
        {
            videoId.GuardNotNull(nameof(videoId));

            if (!ValidateVideoId(videoId))
            {
                throw new ArgumentException($"Invalid YouTube video ID [{videoId}].", nameof(videoId));
            }

            // Get video info
            var videoInfo = GetVideoInfoAsync(videoId); //.ConfigureAwait(false);

            if (videoInfo == null)
            {
                return new List <ClosedCaptionTrackInfo>()
                       {
                       }
            }
            ;

            // Extract captions metadata
            var playerResponseRaw  = videoInfo["player_response"];
            var playerResponseJson = JToken.Parse(playerResponseRaw);
            var captionTracksJson  = playerResponseJson.SelectToken("$..captionTracks").EmptyIfNull();

            // Parse closed caption tracks
            var closedCaptionTrackInfos = new List <ClosedCaptionTrackInfo>();

            foreach (var captionTrackJson in captionTracksJson)
            {
                // Extract values
                var code = captionTrackJson["languageCode"].Value <string>();

                var name     = captionTrackJson["name"]["simpleText"].Value <string>();
                var language = new Language(code, name);
                var isAuto   = captionTrackJson["vssId"].Value <string>()
                               .StartsWith("a.", StringComparison.OrdinalIgnoreCase);
                var url = captionTrackJson["baseUrl"].Value <string>();

                // Enforce format
                url = UrlEx.SetQueryParameter(url, "format", "3");

                var closedCaptionTrackInfo = new ClosedCaptionTrackInfo(url, language, isAuto);
                closedCaptionTrackInfos.Add(closedCaptionTrackInfo);
            }

            return(closedCaptionTrackInfos);
        }
    }
        private void ParseClosedCaptionTrackInfos(string encodedData,
                                                  ICollection <ClosedCaptionTrackInfo> closedCaptionTrackInfos)
        {
            foreach (var captionEncoded in encodedData.Split(","))
            {
                var captionInfoDic = UrlHelper.GetDictionaryFromUrlQuery(captionEncoded);

                var url = captionInfoDic.Get("u");

                var code     = captionInfoDic.Get("lc");
                var name     = captionInfoDic.Get("n");
                var language = new Language(code, name);

                var isAuto = captionInfoDic.Get("v").Contains("a.");

                var closedCaptionTrackInfo = new ClosedCaptionTrackInfo(url, language, isAuto);
                closedCaptionTrackInfos.Add(closedCaptionTrackInfo);
            }
        }
Beispiel #17
0
        /// <inheritdoc />
        public void DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, Stream output,
                                                    //IProgress<double> progress = null,
                                                    CancellationToken cancellationToken = default(CancellationToken))
        {
            info.GuardNotNull(nameof(info));
            output.GuardNotNull(nameof(output));

            // Get the track
            var track = GetClosedCaptionTrackAsync(info); //.ConfigureAwait(false);

            // Save to file as SRT
            //using (var writer = new StreamWriter(output, Encoding.UTF8, 1024, true))
            using (var writer = new StreamWriter(output, Encoding.UTF8, 1024))
            {
                for (var i = 0; i < track.Captions.Count; i++)
                {
                    // Make sure cancellation was not requested
                    cancellationToken.ThrowIfCancellationRequested();

                    var caption = track.Captions[i];
                    var buffer  = new StringBuilder();

                    // Line number
                    buffer.AppendLine((i + 1).ToString());

                    //// Time start --> time end
                    //buffer.Append(caption.Offset.ToString(@"hh\:mm\:ss\,fff"));
                    //buffer.Append(" --> ");
                    //buffer.Append((caption.Offset + caption.Duration).ToString(@"hh\:mm\:ss\,fff"));
                    //buffer.AppendLine();

                    // Actual text
                    buffer.AppendLine(caption.Text);

                    // Write to stream
                    //await writer.WriteLineAsync(buffer.ToString()).ConfigureAwait(false);
                    writer.WriteLine(buffer.ToString());

                    // Report progress
                    //progress?.Report((i + 1.0) / track.Captions.Count);
                }
            }
        }
Beispiel #18
0
        /// <summary>
        /// Gets the actual closed caption track associated with given metadata
        /// and downloads it as SRT file.
        /// </summary>
        public async Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, string filePath,
                                                          IProgress <double> progress, CancellationToken cancellationToken)
        {
            info.GuardNotNull(nameof(info));
            filePath.GuardNotNull(nameof(filePath));

            // Get the track
            var track = await GetClosedCaptionTrackAsync(info).ConfigureAwait(false);

            // Save to file as SRT
            using (var writer = File.CreateText(filePath))
            {
                for (var i = 0; i < track.Captions.Count; i++)
                {
                    // Make sure cancellation was not requested
                    cancellationToken.ThrowIfCancellationRequested();

                    var caption = track.Captions[i];
                    var buffer  = new StringBuilder();

                    // Line number
                    buffer.AppendLine((i + 1).ToString());

                    // Time start --> time end
                    buffer.Append(caption.Offset.ToString(@"hh\:mm\:ss\,fff"));
                    buffer.Append(" --> ");
                    buffer.Append((caption.Offset + caption.Duration).ToString(@"hh\:mm\:ss\,fff"));
                    buffer.AppendLine();

                    // Actual text
                    buffer.AppendLine(caption.Text);

                    // Write to stream
                    await writer.WriteLineAsync(buffer.ToString()).ConfigureAwait(false);

                    // Report progress
                    progress?.Report((i + 1.0) / track.Captions.Count);
                }
            }
        }
        /// <summary>
        /// Gets the actual closed caption track represented by given metadata
        /// </summary>
        public async Task <ClosedCaptionTrack> GetClosedCaptionTrackAsync(ClosedCaptionTrackInfo info)
        {
            info.GuardNotNull(nameof(info));

            // Get manifest
            var response = await _httpService.GetStringAsync(info.Url).ConfigureAwait(false);

            var trackXml = XElement.Parse(response).StripNamespaces();

            // Parse captions
            var captions = new List <ClosedCaption>();

            foreach (var captionXml in trackXml.Descendants("text"))
            {
                var text     = captionXml.Value;
                var offset   = TimeSpan.FromSeconds((double)captionXml.AttributeStrict("start"));
                var duration = TimeSpan.FromSeconds((double)captionXml.AttributeStrict("dur"));

                var caption = new ClosedCaption(text, offset, duration);
                captions.Add(caption);
            }

            return(new ClosedCaptionTrack(info, captions));
        }
Beispiel #20
0
 /// <inheritdoc />
 public async Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, string filePath,
                                                   IProgress <double>?progress = null, CancellationToken cancellationToken = default)
 {
     using var output = File.Create(filePath);
     await DownloadClosedCaptionTrackAsync(info, output, progress, cancellationToken).ConfigureAwait(false);
 }
Beispiel #21
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));
        }
Beispiel #22
0
 /// <summary>
 /// Gets the actual closed caption track associated with given metadata
 /// and downloads it as SRT file.
 /// </summary>
 public Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, string filePath)
 => DownloadClosedCaptionTrackAsync(info, filePath, null);
 public CaptionsPage(string title, string id, ClosedCaptionTrackInfo info)
 {
     InitializeComponent();
     On <Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
     BindingContext = viewModel = new YTCaptionsViewModel(title, id, info);
 }
Beispiel #24
0
 public SubtitleInput(ClosedCaptionTrackInfo info, string filePath)
 {
     Info     = info;
     FilePath = filePath;
 }
Beispiel #25
0
 /// <summary>
 /// Gets the actual closed caption track associated with given metadata
 /// and downloads it as SRT file.
 /// </summary>
 public Task DownloadClosedCaptionTrackAsync(ClosedCaptionTrackInfo info, string filePath,
                                             IProgress <double> progress)
 => DownloadClosedCaptionTrackAsync(info, filePath, progress, CancellationToken.None);
Beispiel #26
0
 public SubtitleDownloadOption(ClosedCaptionTrackInfo trackInfo)
 {
     TrackInfo = trackInfo;
 }