public static string GetClipsRequest(CourseRpcData courseRpcData, string author, string moduleId, int clipIndex) { var course = courseRpcData.Data.Rpc.BootstrapPlayer.Course; return("{\"query\": \"query viewClip {viewClip (input: {author: \\\"" + author + "\\\", " + $"clipIndex: {clipIndex}, " + "courseName: \\\"" + course.Name + "\\\", includeCaptions: " + course.CourseHasCaptions.ToString().ToLowerInvariant() + ", locale: \\\"en\\\", mediaType: \\\"mp4\\\", moduleName: \\\"" + moduleId + "\\\", quality: \\\"" + (course.SupportsWideScreenVideoFormats ? Settings.Default.Resolution1280x720 : Settings.Default.Resolution1024x768) + "\\\"}) {urls {url cdn rank source}, status}}\", \"variables\": {}}"); }
private async Task DownloadModule(Course course, string courseDirectory, int moduleCounter, Module module) { var moduleDirectory = CreateModuleDirectory(courseDirectory, moduleCounter, module.Title); var clipCounter = 0; string referrer = $"https://{Settings.Default.SiteHostName}/player?course={course.Id}&author={module.AuthorId}&name={module.ModuleId}&clip=0&mode=live"; HttpHelper httpHelper = new HttpHelper { AcceptEncoding = string.Empty, AcceptHeader = AcceptHeader.All, ContentType = ContentType.AppJsonUtf8, Cookies = Cookies, Referrer = new Uri(referrer), UserAgent = _userAgent }; string moduleInfoPayload = GraphQlHelper.GetModuleInfoRequest(course.Id); ResponseEx courseRpcResponse = await httpHelper.SendRequest(HttpMethod.Post, new Uri("https://" + Settings.Default.SiteHostName + "/player/api/graphql"), moduleInfoPayload, Settings.Default.RetryOnRequestFailureCount, _token); CourseRpcData courceRpcData = JsonConvert.DeserializeObject <CourseRpcData>(courseRpcResponse.Content); foreach (var clip in module.Clips) { ++clipCounter; var fileName = GetFullFileNameWithoutExtension(clipCounter, moduleDirectory, clip); if (!File.Exists($"{fileName}.{Settings.Default.ClipExtensionMp4}")) { httpHelper.Referrer = new Uri($"https://{Settings.Default.SiteHostName}/player?course={course.Id}&author={module.AuthorId}&name={module.ModuleId}&clip=0&mode=live"); string s = GraphQlHelper.GetClipsRequest(courceRpcData, module.Id.Split('|')[1], module.Id.Split('|')[2], clipCounter - 1); ResponseEx viewclipResponse = await httpHelper.SendRequest(HttpMethod.Post, new Uri("https://" + Settings.Default.SiteHostName + "/player/api/graphql"), s, Settings.Default.RetryOnRequestFailureCount, _token); if (viewclipResponse.Content == "Unauthorized") { throw new UnauthorizedException(Resources.CheckYourSubscription); } var clipData = JsonConvert.DeserializeObject <ClipData>(viewclipResponse.Content); if (course.HasTranscript) { string unformattedSubtitlesJson = await _subtitleService.DownloadAsync(httpHelper, clip.ClipId.ToString(), _token); Caption[] unformattedSubtitles = JsonConvert.DeserializeObject <Caption[]>(unformattedSubtitlesJson); if (unformattedSubtitles.Length > 0) { TimeSpan totalDurationTs = XmlConvert.ToTimeSpan(clip.Duration); IList <SrtRecord> formattedSubtitles = GetFormattedSubtitles(unformattedSubtitles, totalDurationTs); _subtitleService.Write($"{fileName}.{Settings.Default.SubtitilesExtensionMp4}", formattedSubtitles); } } await DownloadClip(clipData.Data.ViewClip.Urls[1].UrlUrl, fileName, clipCounter, course.Modules.Sum(m => m.Clips.Length)); } } }