/// <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); } }
/// <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)); }
/// <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); }
/// <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)); }
/// <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)); }
/// <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; }
/// <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); }
/// <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); }
/// <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)); }
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)) })); }
/// <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); } }
/// <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); } } }
/// <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)); }
/// <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); }
/// <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)); }
/// <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); }
public SubtitleInput(ClosedCaptionTrackInfo info, string filePath) { Info = info; FilePath = filePath; }
/// <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);
public SubtitleDownloadOption(ClosedCaptionTrackInfo trackInfo) { TrackInfo = trackInfo; }