/// <summary> /// Downloads video's embed page and extracts useful data. /// </summary> protected virtual async Task <YouTubeEmbedPageData> DownloadEmbedPage(string id) { var result = new YouTubeEmbedPageData(); // get embed uri var embedUri = GetYouTubeEmbedUri(id); // download embed page var client = HttpHelper.CreateClient(); var embedPageContent = await client.GetStringAsync(embedUri); // find base.js var match = BaseJsLocatorRegex.Match(embedPageContent); if (!match.Success) { throw new GrabParseException("Failed to find base.js script reference."); } result.BaseJsUri = new Uri(embedUri, match.Groups[1].Value); return(result); }
/// <summary> /// Invoked by <see cref="GrabAsync"/> method when the target video has a signature. /// This method downloads the necessary script (base.js) and decipher all grabbed links. /// </summary> protected virtual async Task Decipher(YouTubeEmbedPageData embedPageData, YouTubeMetadata metaData, CancellationToken cancellationToken) { // download base.js var client = HttpHelper.CreateClient(); using var response = await client.GetAsync(embedPageData.BaseJsUri, cancellationToken); var scriptContent = await response.Content.ReadAsStringAsync(); var script = new YouTubeScript(scriptContent); // find decipher function name var match = DecipherFunctionRegex.Match(scriptContent); if (!match.Success) { throw new GrabParseException("Failed to locate decipher function."); } var fn = match.Groups[1].Value; // prepare script host to execute the decipher function along with its used functions script.PrepareDecipherFunctionCall(fn); // call decipher function foreach (var streamInfo in metaData.AllStreams) { if (string.IsNullOrEmpty(streamInfo.Signature)) { continue; } // calculate decipher streamInfo.Decipher = script.CallDecipherFunction(fn, streamInfo.Signature); // update uri streamInfo.Url += $"&sig={Uri.EscapeDataString(streamInfo.Decipher)}"; } }