using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Globalization; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using Avalonia.Remote.Protocol.Input; using CRD.Utils; using CRD.Utils.Structs; using Newtonsoft.Json; namespace CRD.Downloader; public class CrEpisode(){ private readonly Crunchyroll crunInstance = Crunchyroll.Instance; public async Task<CrunchyEpisode?> ParseEpisodeById(string id, string locale){ if (crunInstance.CmsToken?.Cms == null){ Console.Error.WriteLine("Missing CMS Access Token"); return null; } NameValueCollection query = HttpUtility.ParseQueryString(new UriBuilder().Query); query["preferred_audio_language"] = "ja-JP"; query["locale"] = Languages.Locale2language(locale).CrLocale; var request = HttpClientReq.CreateRequestMessage($"{Api.Cms}/episodes/{id}", HttpMethod.Get, true, true, query); var response = await HttpClientReq.Instance.SendHttpRequest(request); if (!response.IsOk){ Console.Error.WriteLine("Series Request Failed"); return null; } CrunchyEpisodeList epsidoe = Helpers.Deserialize<CrunchyEpisodeList>(response.ResponseContent, crunInstance.SettingsJsonSerializerSettings); if (epsidoe.Total < 1){ return null; } if (epsidoe.Total == 1 && epsidoe.Data != null){ return epsidoe.Data.First(); } Console.Error.WriteLine("Multiple episodes returned with one ID?"); if (epsidoe.Data != null) return epsidoe.Data.First(); return null; } public async Task<CrunchyRollEpisodeData> EpisodeData(CrunchyEpisode dlEpisode){ bool serieshasversions = true; // Dictionary<string, EpisodeAndLanguage> episodes = new Dictionary<string, EpisodeAndLanguage>(); CrunchyRollEpisodeData episode = new CrunchyRollEpisodeData(); if (crunInstance.CrunOptions.History){ await crunInstance.CrHistory.UpdateWithEpisode(dlEpisode); } var seasonIdentifier = !string.IsNullOrEmpty(dlEpisode.Identifier) ? dlEpisode.Identifier.Split('|')[1] : $"S{dlEpisode.SeasonNumber}"; episode.Key = $"{seasonIdentifier}E{dlEpisode.Episode ?? (dlEpisode.EpisodeNumber + "")}"; episode.EpisodeAndLanguages = new EpisodeAndLanguage{ Items = new List<CrunchyEpisode>(), Langs = new List<LanguageItem>() }; if (dlEpisode.Versions != null){ foreach (var version in dlEpisode.Versions){ // Ensure there is only one of the same language if (episode.EpisodeAndLanguages.Langs.All(a => a.CrLocale != version.AudioLocale)){ // Push to arrays if there are no duplicates of the same language episode.EpisodeAndLanguages.Items.Add(dlEpisode); episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == version.AudioLocale)); } } } else{ // Episode didn't have versions, mark it as such to be logged. serieshasversions = false; // Ensure there is only one of the same language if (episode.EpisodeAndLanguages.Langs.All(a => a.CrLocale != dlEpisode.AudioLocale)){ // Push to arrays if there are no duplicates of the same language episode.EpisodeAndLanguages.Items.Add(dlEpisode); episode.EpisodeAndLanguages.Langs.Add(Array.Find(Languages.languages, a => a.CrLocale == dlEpisode.AudioLocale)); } } int specialIndex = 1; int epIndex = 1; var isSpecial = !Regex.IsMatch(episode.EpisodeAndLanguages.Items[0].Episode ?? string.Empty, @"^\d+$"); // Checking if the episode is not a number (i.e., special). string newKey; if (isSpecial && !string.IsNullOrEmpty(episode.EpisodeAndLanguages.Items[0].Episode)){ newKey = episode.EpisodeAndLanguages.Items[0].Episode ?? "SP" + (specialIndex + " " + episode.EpisodeAndLanguages.Items[0].Id); } else{ newKey = $"{(isSpecial ? "SP" : 'E')}{(isSpecial ? (specialIndex + " " + episode.EpisodeAndLanguages.Items[0].Id) : episode.EpisodeAndLanguages.Items[0].Episode ?? epIndex + "")}"; } episode.Key = newKey; var seasonTitle = episode.EpisodeAndLanguages.Items.FirstOrDefault(a => !Regex.IsMatch(a.SeasonTitle, @"\(\w+ Dub\)")).SeasonTitle ?? Regex.Replace(episode.EpisodeAndLanguages.Items[0].SeasonTitle, @"\(\w+ Dub\)", "").TrimEnd(); var title = episode.EpisodeAndLanguages.Items[0].Title; var seasonNumber = Helpers.ExtractNumberAfterS(episode.EpisodeAndLanguages.Items[0].Identifier) ?? episode.EpisodeAndLanguages.Items[0].SeasonNumber.ToString(); var languages = episode.EpisodeAndLanguages.Items.Select((a, index) => $"{(a.IsPremiumOnly ? "+ " : "")}{episode.EpisodeAndLanguages.Langs.ElementAtOrDefault(index).Name ?? "Unknown"}").ToArray(); //☆ Console.WriteLine($"[{episode.Key}] {seasonTitle} - Season {seasonNumber} - {title} [{string.Join(", ", languages)}]"); if (!serieshasversions){ Console.WriteLine("Couldn\'t find versions on episode, fell back to old method."); } // crunchySeriesList.Data = sortedEpisodes; // // // var images = (episode.EpisodeAndLanguages.Items[0].Images?.Thumbnail ?? new List<List<Image>>{ new List<Image>{ new Image{ Source = "/notFound.png" } } }); // var seconds = (int)Math.Floor(episode.EpisodeAndLanguages.Items[0].DurationMs / 1000.0); // // var newEpisode = new Episode{ // E = episode.Key.StartsWith("E") ? episode.Key.Substring(1) : episode.Key, // Lang = episode.EpisodeAndLanguages.Langs.Select(a => a.Code).ToList(), // Name = episode.EpisodeAndLanguages.Items[0].Title, // Season = Helpers.ExtractNumberAfterS(episode.EpisodeAndLanguages.Items[0].Identifier) ?? episode.EpisodeAndLanguages.Items[0].SeasonNumber.ToString(), // SeriesTitle = Regex.Replace(episode.EpisodeAndLanguages.Items[0].SeriesTitle, @"\(\w+ Dub\)", "").TrimEnd(), // SeasonTitle = Regex.Replace(episode.EpisodeAndLanguages.Items[0].SeasonTitle, @"\(\w+ Dub\)", "").TrimEnd(), // EpisodeNum = episode.EpisodeAndLanguages.Items[0].EpisodeNumber?.ToString() ?? episode.EpisodeAndLanguages.Items[0].Episode ?? "?", // Id = episode.EpisodeAndLanguages.Items[0].SeasonId, // Img = images[images.Count / 2].FirstOrDefault().Source, // Description = episode.EpisodeAndLanguages.Items[0].Description, // Time = $"{seconds / 60}:{seconds % 60:D2}" // Ensures two digits for seconds. // }; // // CrunchySeriesList crunchySeriesList = new CrunchySeriesList(); return episode; } public CrunchyEpMeta EpisodeMeta(CrunchyRollEpisodeData episodeP, List<string> dubLang){ // var ret = new Dictionary<string, CrunchyEpMeta>(); var retMeta = new CrunchyEpMeta(); for (int index = 0; index < episodeP.EpisodeAndLanguages.Items.Count; index++){ var item = episodeP.EpisodeAndLanguages.Items[index]; if (!dubLang.Contains(episodeP.EpisodeAndLanguages.Langs[index].CrLocale)) continue; item.HideSeasonTitle = true; if (string.IsNullOrEmpty(item.SeasonTitle) && !string.IsNullOrEmpty(item.SeriesTitle)){ item.SeasonTitle = item.SeriesTitle; item.HideSeasonTitle = false; item.HideSeasonNumber = true; } if (string.IsNullOrEmpty(item.SeasonTitle) && string.IsNullOrEmpty(item.SeriesTitle)){ item.SeasonTitle = "NO_TITLE"; item.SeriesTitle = "NO_TITLE"; } var epNum = episodeP.Key.StartsWith('E') ? episodeP.Key[1..] : episodeP.Key; var images = (item.Images?.Thumbnail ?? new List<List<Image>>{ new List<Image>{ new Image{ Source = "/notFound.png" } } }); Regex dubPattern = new Regex(@"\(\w+ Dub\)"); var epMeta = new CrunchyEpMeta(); epMeta.Data = new List<CrunchyEpMetaData>{ new(){ MediaId = item.Id, Versions = item.Versions, IsSubbed = item.IsSubbed, IsDubbed = item.IsDubbed } }; epMeta.SeriesTitle = episodeP.EpisodeAndLanguages.Items.FirstOrDefault(a => !dubPattern.IsMatch(a.SeriesTitle)).SeriesTitle ?? Regex.Replace(episodeP.EpisodeAndLanguages.Items[0].SeriesTitle, @"\(\w+ Dub\)", "").TrimEnd(); epMeta.SeasonTitle = episodeP.EpisodeAndLanguages.Items.FirstOrDefault(a => !dubPattern.IsMatch(a.SeasonTitle)).SeasonTitle ?? Regex.Replace(episodeP.EpisodeAndLanguages.Items[0].SeasonTitle, @"\(\w+ Dub\)", "").TrimEnd(); epMeta.EpisodeNumber = item.Episode; epMeta.EpisodeTitle = item.Title; epMeta.SeasonId = item.SeasonId; epMeta.Season = Helpers.ExtractNumberAfterS(item.Identifier) ?? item.SeasonNumber + ""; epMeta.ShowId = item.SeriesId; epMeta.AbsolutEpisodeNumberE = epNum; epMeta.Image = images[images.Count / 2].FirstOrDefault().Source; epMeta.DownloadProgress = new DownloadProgress(){ IsDownloading = false, Done = false, Error = false, Percent = 0, Time = 0, DownloadSpeed = 0 }; epMeta.AvailableSubs = item.SubtitleLocales; epMeta.Description = item.Description; if (episodeP.EpisodeAndLanguages.Langs.Count > 0){ epMeta.SelectedDubs = dubLang .Where(language => episodeP.EpisodeAndLanguages.Langs.Any(epLang => epLang.CrLocale == language)) .ToList(); } var epMetaData = epMeta.Data[0]; if (!string.IsNullOrEmpty(item.StreamsLink)){ epMetaData.Playback = item.StreamsLink; if (string.IsNullOrEmpty(item.Playback)){ item.Playback = item.StreamsLink; } } if (retMeta.Data != null){ epMetaData.Lang = episodeP.EpisodeAndLanguages.Langs[index]; retMeta.Data.Add(epMetaData); } else{ epMetaData.Lang = episodeP.EpisodeAndLanguages.Langs[index]; epMeta.Data[0] = epMetaData; retMeta = epMeta; } // show ep item.SeqId = epNum; } return retMeta; } }