diff --git a/CRD/Downloader/CalendarManager.cs b/CRD/Downloader/CalendarManager.cs new file mode 100644 index 0000000..ce83426 --- /dev/null +++ b/CRD/Downloader/CalendarManager.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +using CRD.Downloader.Crunchyroll; +using CRD.Utils; +using CRD.Utils.Structs; +using HtmlAgilityPack; + +namespace CRD.Downloader; + +public class CalendarManager{ + #region Calendar Variables + + private Dictionary calendar = new(); + + private Dictionary calendarLanguage = new(){ + { "en-us", "https://www.crunchyroll.com/simulcastcalendar" }, + { "es", "https://www.crunchyroll.com/es/simulcastcalendar" }, + { "es-es", "https://www.crunchyroll.com/es-es/simulcastcalendar" }, + { "pt-br", "https://www.crunchyroll.com/pt-br/simulcastcalendar" }, + { "pt-pt", "https://www.crunchyroll.com/pt-pt/simulcastcalendar" }, + { "fr", "https://www.crunchyroll.com/fr/simulcastcalendar" }, + { "de", "https://www.crunchyroll.com/de/simulcastcalendar" }, + { "ar", "https://www.crunchyroll.com/ar/simulcastcalendar" }, + { "it", "https://www.crunchyroll.com/it/simulcastcalendar" }, + { "ru", "https://www.crunchyroll.com/ru/simulcastcalendar" }, + { "hi", "https://www.crunchyroll.com/hi/simulcastcalendar" }, + }; + + #endregion + + + #region Singelton + + private static CalendarManager? _instance; + private static readonly object Padlock = new(); + + public static CalendarManager Instance{ + get{ + if (_instance == null){ + lock (Padlock){ + if (_instance == null){ + _instance = new CalendarManager(); + } + } + } + + return _instance; + } + } + + #endregion + + + public async Task GetCalendarForDate(string weeksMondayDate, bool forceUpdate){ + if (!forceUpdate && calendar.TryGetValue(weeksMondayDate, out var forDate)){ + return forDate; + } + + var request = calendarLanguage.ContainsKey(CrunchyrollManager.Instance.CrunOptions.SelectedCalendarLanguage ?? "de") + ? HttpClientReq.CreateRequestMessage($"{calendarLanguage[CrunchyrollManager.Instance.CrunOptions.SelectedCalendarLanguage ?? "de"]}?filter=premium&date={weeksMondayDate}", HttpMethod.Get, false, false, null) + : HttpClientReq.CreateRequestMessage($"{calendarLanguage["en-us"]}?filter=premium&date={weeksMondayDate}", HttpMethod.Get, false, false, null); + + + request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"); + request.Headers.AcceptEncoding.ParseAdd("gzip, deflate, br"); + + var response = await HttpClientReq.Instance.SendHttpRequest(request); + + CalendarWeek week = new CalendarWeek(); + week.CalendarDays = new List(); + + // Load the HTML content from a file + HtmlDocument doc = new HtmlDocument(); + doc.LoadHtml(WebUtility.HtmlDecode(response.ResponseContent)); + + // Select each 'li' element with class 'day' + var dayNodes = doc.DocumentNode.SelectNodes("//li[contains(@class, 'day')]"); + + if (dayNodes != null){ + foreach (var day in dayNodes){ + // Extract the date and day name + var date = day.SelectSingleNode(".//time[@datetime]")?.GetAttributeValue("datetime", "No date"); + DateTime dayDateTime = DateTime.Parse(date, null, DateTimeStyles.RoundtripKind); + + if (week.FirstDayOfWeek == null){ + week.FirstDayOfWeek = dayDateTime; + week.FirstDayOfWeekString = dayDateTime.ToString("yyyy-MM-dd"); + } + + var dayName = day.SelectSingleNode(".//h1[@class='day-name']/time")?.InnerText.Trim(); + + CalendarDay calDay = new CalendarDay(); + + calDay.CalendarEpisodes = new List(); + calDay.DayName = dayName; + calDay.DateTime = dayDateTime; + + // Iterate through each episode listed under this day + var episodes = day.SelectNodes(".//article[contains(@class, 'release')]"); + if (episodes != null){ + foreach (var episode in episodes){ + var episodeTimeStr = episode.SelectSingleNode(".//time[contains(@class, 'available-time')]")?.GetAttributeValue("datetime", null); + DateTime episodeTime = DateTime.Parse(episodeTimeStr, null, DateTimeStyles.RoundtripKind); + var hasPassed = DateTime.Now > episodeTime; + + var episodeName = episode.SelectSingleNode(".//h1[contains(@class, 'episode-name')]")?.SelectSingleNode(".//cite[@itemprop='name']")?.InnerText.Trim(); + var seasonLink = episode.SelectSingleNode(".//a[contains(@class, 'js-season-name-link')]")?.GetAttributeValue("href", "No link"); + var episodeLink = episode.SelectSingleNode(".//a[contains(@class, 'available-episode-link')]")?.GetAttributeValue("href", "No link"); + var thumbnailUrl = episode.SelectSingleNode(".//img[contains(@class, 'thumbnail')]")?.GetAttributeValue("src", "No image"); + var isPremiumOnly = episode.SelectSingleNode(".//svg[contains(@class, 'premium-flag')]") != null; + var isPremiere = episode.SelectSingleNode(".//div[contains(@class, 'premiere-flag')]") != null; + var seasonName = episode.SelectSingleNode(".//a[contains(@class, 'js-season-name-link')]")?.SelectSingleNode(".//cite[@itemprop='name']")?.InnerText.Trim(); + var episodeNumber = episode.SelectSingleNode(".//meta[contains(@itemprop, 'episodeNumber')]")?.GetAttributeValue("content", "?"); + + CalendarEpisode calEpisode = new CalendarEpisode(); + + calEpisode.DateTime = episodeTime; + calEpisode.HasPassed = hasPassed; + calEpisode.EpisodeName = episodeName; + calEpisode.SeriesUrl = seasonLink; + calEpisode.EpisodeUrl = episodeLink; + calEpisode.ThumbnailUrl = thumbnailUrl; + calEpisode.IsPremiumOnly = isPremiumOnly; + calEpisode.IsPremiere = isPremiere; + calEpisode.SeasonName = seasonName; + calEpisode.EpisodeNumber = episodeNumber; + + calDay.CalendarEpisodes.Add(calEpisode); + } + } + + week.CalendarDays.Add(calDay); + } + } else{ + Console.Error.WriteLine("No days found in the HTML document."); + } + + calendar[weeksMondayDate] = week; + + + return week; + } + + + public async Task BuildCustomCalendar(bool forceUpdate){ + Console.WriteLine("C" + DateTime.Now.ToString("yyyy-MM-dd")); + + if (!forceUpdate && calendar.TryGetValue("C" + DateTime.Now.ToString("yyyy-MM-dd"), out var forDate)){ + return forDate; + } + + + + CalendarWeek week = new CalendarWeek(); + week.CalendarDays = new List(); + + DateTime today = DateTime.Now; + + for (int i = 0; i < 7; i++){ + CalendarDay calDay = new CalendarDay(); + + calDay.CalendarEpisodes = new List(); + calDay.DateTime = today.AddDays(-i); + calDay.DayName = calDay.DateTime.Value.DayOfWeek.ToString(); + + week.CalendarDays.Add(calDay); + } + + week.CalendarDays.Reverse(); + + var firstDayOfWeek = week.CalendarDays.First().DateTime; + + var newEpisodesBase = await CrunchyrollManager.Instance.CrEpisode.GetNewEpisodes(CrunchyrollManager.Instance.CrunOptions.HistoryLang, 200,firstDayOfWeek, true); + + if (newEpisodesBase is{ Data.Count: > 0 }){ + var newEpisodes = newEpisodesBase.Data; + + foreach (var crBrowseEpisode in newEpisodes){ + var targetDate = CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate ? crBrowseEpisode.EpisodeMetadata.EpisodeAirDate : crBrowseEpisode.LastPublic; + + if (CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs && crBrowseEpisode.EpisodeMetadata.SeasonTitle != null && + (crBrowseEpisode.EpisodeMetadata.SeasonTitle.EndsWith("Dub)") || crBrowseEpisode.EpisodeMetadata.AudioLocale != Locale.JaJp)){ + continue; + } + + var dubFilter = CrunchyrollManager.Instance.CrunOptions.CalendarDubFilter; + if (!string.IsNullOrEmpty(dubFilter) && dubFilter != "none"){ + if (crBrowseEpisode.EpisodeMetadata.AudioLocale != null && crBrowseEpisode.EpisodeMetadata.AudioLocale.GetEnumMemberValue() != dubFilter){ + continue; + } + } + + var calendarDay = (from day in week.CalendarDays + where day.DateTime.HasValue && day.DateTime.Value.Date == targetDate.Date + select day).FirstOrDefault(); + + if (calendarDay != null){ + CalendarEpisode calEpisode = new CalendarEpisode(); + + calEpisode.DateTime = targetDate; + calEpisode.HasPassed = DateTime.Now > targetDate; + calEpisode.EpisodeName = crBrowseEpisode.Title; + calEpisode.SeriesUrl = $"https://www.crunchyroll.com/{CrunchyrollManager.Instance.CrunOptions.HistoryLang}/series/" + crBrowseEpisode.EpisodeMetadata.SeriesId; + calEpisode.EpisodeUrl = $"https://www.crunchyroll.com/{CrunchyrollManager.Instance.CrunOptions.HistoryLang}/watch/{crBrowseEpisode.Id}/"; + calEpisode.ThumbnailUrl = crBrowseEpisode.Images.Thumbnail.First().First().Source; + calEpisode.IsPremiumOnly = crBrowseEpisode.EpisodeMetadata.IsPremiumOnly; + calEpisode.IsPremiere = crBrowseEpisode.EpisodeMetadata.Episode == "1"; + calEpisode.SeasonName = crBrowseEpisode.EpisodeMetadata.SeasonTitle; + calEpisode.EpisodeNumber = crBrowseEpisode.EpisodeMetadata.Episode; + + calendarDay.CalendarEpisodes?.Add(calEpisode); + } + } + } + + + foreach (var day in week.CalendarDays){ + if (day.CalendarEpisodes != null) day.CalendarEpisodes = day.CalendarEpisodes.OrderBy(e => e.DateTime).ToList(); + } + + calendar["C" + DateTime.Now.ToString("yyyy-MM-dd")] = week; + + + return week; + } +} \ No newline at end of file diff --git a/CRD/Downloader/CRAuth.cs b/CRD/Downloader/Crunchyroll/CRAuth.cs similarity index 98% rename from CRD/Downloader/CRAuth.cs rename to CRD/Downloader/Crunchyroll/CRAuth.cs index 916a25a..205dd13 100644 --- a/CRD/Downloader/CRAuth.cs +++ b/CRD/Downloader/Crunchyroll/CRAuth.cs @@ -9,13 +9,12 @@ using System.Web; using CRD.Utils; using CRD.Utils.Structs; using Newtonsoft.Json; -using YamlDotNet.Core.Tokens; -namespace CRD.Downloader; +namespace CRD.Downloader.Crunchyroll; public class CrAuth{ - private readonly Crunchyroll crunInstance = Crunchyroll.Instance; + private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; public async Task AuthAnonymous(){ var formData = new Dictionary{ @@ -46,7 +45,7 @@ public class CrAuth{ PreferredContentSubtitleLanguage = "de-DE" }; - Crunchyroll.Instance.CmsToken = new CrCmsToken(); + CrunchyrollManager.Instance.CmsToken = new CrCmsToken(); } diff --git a/CRD/Downloader/CrEpisode.cs b/CRD/Downloader/Crunchyroll/CrEpisode.cs similarity index 93% rename from CRD/Downloader/CrEpisode.cs rename to CRD/Downloader/Crunchyroll/CrEpisode.cs index 5bb50df..60ca5da 100644 --- a/CRD/Downloader/CrEpisode.cs +++ b/CRD/Downloader/Crunchyroll/CrEpisode.cs @@ -1,21 +1,18 @@ 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; +namespace CRD.Downloader.Crunchyroll; public class CrEpisode(){ - private readonly Crunchyroll crunInstance = Crunchyroll.Instance; + private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; public async Task ParseEpisodeById(string id, string crLocale, bool forcedLang = false){ if (crunInstance.CmsToken?.Cms == null){ @@ -67,11 +64,11 @@ public class CrEpisode(){ CrunchyRollEpisodeData episode = new CrunchyRollEpisodeData(); if (crunInstance.CrunOptions.History && updateHistory){ - await crunInstance.CrHistory.UpdateWithEpisode(dlEpisode); + await crunInstance.History.UpdateWithEpisode(dlEpisode); var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == dlEpisode.SeriesId); if (historySeries != null){ - Crunchyroll.Instance.CrHistory.MatchHistorySeriesWithSonarr(false); - await Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(false, historySeries); + CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(false); + await CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, historySeries); CfgManager.UpdateHistoryFile(); } } @@ -245,7 +242,7 @@ public class CrEpisode(){ return retMeta; } - public async Task GetNewEpisodes(string? crLocale, int requestAmount , bool forcedLang = false){ + public async Task GetNewEpisodes(string? crLocale, int requestAmount,DateTime? firstWeekDay = null , bool forcedLang = false){ CrBrowseEpisodeBase? complete = new CrBrowseEpisodeBase(); complete.Data =[]; @@ -279,13 +276,21 @@ public class CrEpisode(){ if (series != null){ complete.Total = series.Total; - if (series.Data != null) complete.Data.AddRange(series.Data); + if (series.Data != null){ + complete.Data.AddRange(series.Data); + if (firstWeekDay != null){ + if (firstWeekDay.Value.Date <= series.Data.Last().LastPublic && i + 50 == requestAmount){ + requestAmount += 50; + } + } + + } } else{ break; } i += 50; - } while (i < requestAmount); + } while (i < requestAmount && requestAmount < 500); return complete; diff --git a/CRD/Downloader/CrSeries.cs b/CRD/Downloader/Crunchyroll/CrSeries.cs similarity index 97% rename from CRD/Downloader/CrSeries.cs rename to CRD/Downloader/Crunchyroll/CrSeries.cs index c6fdf2c..d22ac27 100644 --- a/CRD/Downloader/CrSeries.cs +++ b/CRD/Downloader/Crunchyroll/CrSeries.cs @@ -1,24 +1,20 @@ using System; using System.Collections.Generic; using System.Collections.Specialized; -using System.Globalization; using System.Linq; using System.Net.Http; -using System.Net.Http.Headers; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using CRD.Utils; using CRD.Utils.Structs; using CRD.Views; -using DynamicData; -using Newtonsoft.Json; using ReactiveUI; -namespace CRD.Downloader; +namespace CRD.Downloader.Crunchyroll; public class CrSeries(){ - private readonly Crunchyroll crunInstance = Crunchyroll.Instance; + private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; public async Task> DownloadFromSeriesId(string id, CrunchyMultiDownload data){ var series = await ListSeriesId(id, "", data); @@ -57,7 +53,7 @@ public class CrSeries(){ } if (crunInstance.CrunOptions.History){ - var dubLangList = crunInstance.CrHistory.GetDubList(item.SeriesId, item.SeasonId); + var dubLangList = crunInstance.History.GetDubList(item.SeriesId, item.SeasonId); if (dubLangList.Count > 0){ dubLang = dubLangList; } @@ -164,7 +160,7 @@ public class CrSeries(){ var seasonData = await GetSeasonDataById(s.Id, ""); if (seasonData.Data != null){ if (crunInstance.CrunOptions.History){ - crunInstance.CrHistory.UpdateWithSeasonData(seasonData,false); + crunInstance.History.UpdateWithSeasonData(seasonData,false); } foreach (var episode in seasonData.Data){ @@ -214,8 +210,8 @@ public class CrSeries(){ if (crunInstance.CrunOptions.History){ var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == id); if (historySeries != null){ - crunInstance.CrHistory.MatchHistorySeriesWithSonarr(false); - await crunInstance.CrHistory.MatchHistoryEpisodesWithSonarr(false, historySeries); + crunInstance.History.MatchHistorySeriesWithSonarr(false); + await crunInstance.History.MatchHistoryEpisodesWithSonarr(false, historySeries); CfgManager.UpdateHistoryFile(); } } diff --git a/CRD/Downloader/Crunchyroll.cs b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs similarity index 84% rename from CRD/Downloader/Crunchyroll.cs rename to CRD/Downloader/Crunchyroll/CrunchyrollManager.cs index 52cc000..1278a8e 100644 --- a/CRD/Downloader/Crunchyroll.cs +++ b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Collections.Specialized; using System.Globalization; using System.IO; using System.Linq; -using System.Net; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; @@ -13,62 +11,27 @@ using System.Threading.Tasks; using System.Xml; using Avalonia.Media; using CRD.Utils; -using CRD.Utils.CustomList; using CRD.Utils.DRM; using CRD.Utils.Files; using CRD.Utils.HLS; using CRD.Utils.Muxing; using CRD.Utils.Sonarr; -using CRD.Utils.Sonarr.Models; using CRD.Utils.Structs; using CRD.Utils.Structs.History; -using CRD.ViewModels; using CRD.Views; -using HtmlAgilityPack; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using ReactiveUI; using LanguageItem = CRD.Utils.Structs.LanguageItem; -namespace CRD.Downloader; +namespace CRD.Downloader.Crunchyroll; -public class Crunchyroll{ +public class CrunchyrollManager{ public CrToken? Token; public CrCmsToken? CmsToken; public CrProfile Profile = new(); public CrDownloadOptions CrunOptions; - - #region Download Variables - - public RefreshableObservableCollection Queue = new RefreshableObservableCollection(); - public ObservableCollection DownloadItemModels = new ObservableCollection(); - public int ActiveDownloads; - - #endregion - - - #region Calendar Variables - - private Dictionary calendar = new(); - - private Dictionary calendarLanguage = new(){ - { "en-us", "https://www.crunchyroll.com/simulcastcalendar" }, - { "es", "https://www.crunchyroll.com/es/simulcastcalendar" }, - { "es-es", "https://www.crunchyroll.com/es-es/simulcastcalendar" }, - { "pt-br", "https://www.crunchyroll.com/pt-br/simulcastcalendar" }, - { "pt-pt", "https://www.crunchyroll.com/pt-pt/simulcastcalendar" }, - { "fr", "https://www.crunchyroll.com/fr/simulcastcalendar" }, - { "de", "https://www.crunchyroll.com/de/simulcastcalendar" }, - { "ar", "https://www.crunchyroll.com/ar/simulcastcalendar" }, - { "it", "https://www.crunchyroll.com/it/simulcastcalendar" }, - { "ru", "https://www.crunchyroll.com/ru/simulcastcalendar" }, - { "hi", "https://www.crunchyroll.com/hi/simulcastcalendar" }, - }; - - #endregion - - + #region History Variables public ObservableCollection HistoryList = new(); @@ -76,9 +39,7 @@ public class Crunchyroll{ public HistorySeries SelectedSeries = new HistorySeries{ Seasons =[] }; - - public List SonarrSeries =[]; - + #endregion @@ -93,19 +54,19 @@ public class Crunchyroll{ public CrAuth CrAuth; public CrEpisode CrEpisode; public CrSeries CrSeries; - public History CrHistory; + public History History; #region Singelton - private static Crunchyroll? _instance; + private static CrunchyrollManager? _instance; private static readonly object Padlock = new(); - public static Crunchyroll Instance{ + public static CrunchyrollManager Instance{ get{ if (_instance == null){ lock (Padlock){ if (_instance == null){ - _instance = new Crunchyroll(); + _instance = new CrunchyrollManager(); } } } @@ -116,9 +77,8 @@ public class Crunchyroll{ #endregion - public Crunchyroll(){ + public CrunchyrollManager(){ CrunOptions = new CrDownloadOptions(); - Queue.CollectionChanged += UpdateItemListOnRemove; } public void InitOptions(){ @@ -159,7 +119,7 @@ public class Crunchyroll{ CrAuth = new CrAuth(); CrEpisode = new CrEpisode(); CrSeries = new CrSeries(); - CrHistory = new History(); + History = new History(); Profile = new CrProfile{ Username = "???", @@ -204,267 +164,15 @@ public class Crunchyroll{ HistoryList =[]; } - RefreshSonarr(); - } - } - } - - public async void RefreshSonarr(){ - await SonarrClient.Instance.CheckSonarrSettings(); - if (CrunOptions.SonarrProperties is{ SonarrEnabled: true }){ - SonarrSeries = await SonarrClient.Instance.GetSeries(); - CrHistory.MatchHistorySeriesWithSonarr(true); - } - } - - private void UpdateItemListOnRemove(object? sender, NotifyCollectionChangedEventArgs e){ - if (e.Action == NotifyCollectionChangedAction.Remove){ - if (e.OldItems != null) - foreach (var eOldItem in e.OldItems){ - var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(eOldItem)); - if (downloadItem != null){ - DownloadItemModels.Remove(downloadItem); - } else{ - Console.Error.WriteLine("Failed to Remove Episode from list"); - } - } - } - - UpdateDownloadListItems(); - } - - public void UpdateDownloadListItems(){ - var list = Queue; - - foreach (CrunchyEpMeta crunchyEpMeta in list){ - var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(crunchyEpMeta)); - if (downloadItem != null){ - downloadItem.Refresh(); - } else{ - downloadItem = new DownloadItemModel(crunchyEpMeta); - downloadItem.LoadImage(); - DownloadItemModels.Add(downloadItem); - } - - if (downloadItem is{ isDownloading: false, Error: false } && CrunOptions.AutoDownload && ActiveDownloads < CrunOptions.SimultaneousDownloads){ - downloadItem.StartDownload(); + SonarrClient.Instance.RefreshSonarr(); } } } - public async Task GetCalendarForDate(string weeksMondayDate, bool forceUpdate){ - if (!forceUpdate && calendar.TryGetValue(weeksMondayDate, out var forDate)){ - return forDate; - } - - var request = calendarLanguage.ContainsKey(CrunOptions.SelectedCalendarLanguage ?? "de") - ? HttpClientReq.CreateRequestMessage($"{calendarLanguage[CrunOptions.SelectedCalendarLanguage ?? "de"]}?filter=premium&date={weeksMondayDate}", HttpMethod.Get, false, false, null) - : HttpClientReq.CreateRequestMessage($"{calendarLanguage["en-us"]}?filter=premium&date={weeksMondayDate}", HttpMethod.Get, false, false, null); - - - request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"); - request.Headers.AcceptEncoding.ParseAdd("gzip, deflate, br"); - - var response = await HttpClientReq.Instance.SendHttpRequest(request); - - CalendarWeek week = new CalendarWeek(); - week.CalendarDays = new List(); - - // Load the HTML content from a file - HtmlDocument doc = new HtmlDocument(); - doc.LoadHtml(WebUtility.HtmlDecode(response.ResponseContent)); - - // Select each 'li' element with class 'day' - var dayNodes = doc.DocumentNode.SelectNodes("//li[contains(@class, 'day')]"); - - if (dayNodes != null){ - foreach (var day in dayNodes){ - // Extract the date and day name - var date = day.SelectSingleNode(".//time[@datetime]")?.GetAttributeValue("datetime", "No date"); - DateTime dayDateTime = DateTime.Parse(date, null, DateTimeStyles.RoundtripKind); - - if (week.FirstDayOfWeek == null){ - week.FirstDayOfWeek = dayDateTime; - week.FirstDayOfWeekString = dayDateTime.ToString("yyyy-MM-dd"); - } - - var dayName = day.SelectSingleNode(".//h1[@class='day-name']/time")?.InnerText.Trim(); - - CalendarDay calDay = new CalendarDay(); - - calDay.CalendarEpisodes = new List(); - calDay.DayName = dayName; - calDay.DateTime = dayDateTime; - - // Iterate through each episode listed under this day - var episodes = day.SelectNodes(".//article[contains(@class, 'release')]"); - if (episodes != null){ - foreach (var episode in episodes){ - var episodeTimeStr = episode.SelectSingleNode(".//time[contains(@class, 'available-time')]")?.GetAttributeValue("datetime", null); - DateTime episodeTime = DateTime.Parse(episodeTimeStr, null, DateTimeStyles.RoundtripKind); - var hasPassed = DateTime.Now > episodeTime; - - var episodeName = episode.SelectSingleNode(".//h1[contains(@class, 'episode-name')]")?.SelectSingleNode(".//cite[@itemprop='name']")?.InnerText.Trim(); - var seasonLink = episode.SelectSingleNode(".//a[contains(@class, 'js-season-name-link')]")?.GetAttributeValue("href", "No link"); - var episodeLink = episode.SelectSingleNode(".//a[contains(@class, 'available-episode-link')]")?.GetAttributeValue("href", "No link"); - var thumbnailUrl = episode.SelectSingleNode(".//img[contains(@class, 'thumbnail')]")?.GetAttributeValue("src", "No image"); - var isPremiumOnly = episode.SelectSingleNode(".//svg[contains(@class, 'premium-flag')]") != null; - var isPremiere = episode.SelectSingleNode(".//div[contains(@class, 'premiere-flag')]") != null; - var seasonName = episode.SelectSingleNode(".//a[contains(@class, 'js-season-name-link')]")?.SelectSingleNode(".//cite[@itemprop='name']")?.InnerText.Trim(); - var episodeNumber = episode.SelectSingleNode(".//meta[contains(@itemprop, 'episodeNumber')]")?.GetAttributeValue("content", "?"); - - CalendarEpisode calEpisode = new CalendarEpisode(); - - calEpisode.DateTime = episodeTime; - calEpisode.HasPassed = hasPassed; - calEpisode.EpisodeName = episodeName; - calEpisode.SeriesUrl = seasonLink; - calEpisode.EpisodeUrl = episodeLink; - calEpisode.ThumbnailUrl = thumbnailUrl; - calEpisode.IsPremiumOnly = isPremiumOnly; - calEpisode.IsPremiere = isPremiere; - calEpisode.SeasonName = seasonName; - calEpisode.EpisodeNumber = episodeNumber; - - calDay.CalendarEpisodes.Add(calEpisode); - } - } - - week.CalendarDays.Add(calDay); - } - } else{ - Console.Error.WriteLine("No days found in the HTML document."); - } - - calendar[weeksMondayDate] = week; - - - return week; - } - - public async Task AddEpisodeToQue(string epId, string crLocale, List dubLang, bool updateHistory = false){ - await CrAuth.RefreshToken(true); - - var episodeL = await CrEpisode.ParseEpisodeById(epId, crLocale); - - - if (episodeL != null){ - if (episodeL.Value.IsPremiumOnly && !Profile.HasPremium){ - MessageBus.Current.SendMessage(new ToastMessage($"Episode is a premium episode – make sure that you are signed in with an account that has an active premium subscription", ToastType.Error, 3)); - return; - } - - var sList = await CrEpisode.EpisodeData((CrunchyEpisode)episodeL, updateHistory); - - (HistoryEpisode? historyEpisode, List dublist, string downloadDirPath) historyEpisode = (null, [], ""); - - if (CrunOptions.History){ - var episode = sList.EpisodeAndLanguages.Items.First(); - historyEpisode = CrHistory.GetHistoryEpisodeWithDubListAndDownloadDir(episode.SeriesId, episode.SeasonId, episode.Id); - if (historyEpisode.dublist.Count > 0){ - dubLang = historyEpisode.dublist; - } - } - - - var selected = CrEpisode.EpisodeMeta(sList, dubLang); - - if (CrunOptions.IncludeVideoDescription){ - if (selected.Data is{ Count: > 0 }){ - var episode = await CrEpisode.ParseEpisodeById(selected.Data.First().MediaId, string.IsNullOrEmpty(CrunOptions.DescriptionLang) ? DefaultLocale : CrunOptions.DescriptionLang, true); - selected.Description = episode?.Description ?? selected.Description; - } - } - - if (selected.Data is{ Count: > 0 }){ - if (CrunOptions.History){ - // var historyEpisode = CrHistory.GetHistoryEpisodeWithDownloadDir(selected.ShowId, selected.SeasonId, selected.Data.First().MediaId); - if (CrunOptions.SonarrProperties is{ SonarrEnabled: true, UseSonarrNumbering: true }){ - if (historyEpisode.historyEpisode != null){ - if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrEpisodeNumber)){ - selected.EpisodeNumber = historyEpisode.historyEpisode.SonarrEpisodeNumber; - } - - if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrSeasonNumber)){ - selected.Season = historyEpisode.historyEpisode.SonarrSeasonNumber; - } - } - } - - if (!string.IsNullOrEmpty(historyEpisode.downloadDirPath)){ - selected.DownloadPath = historyEpisode.downloadDirPath; - } - } - - selected.DownloadSubs = CrunOptions.DlSubs; - Queue.Add(selected); - - - if (selected.Data.Count < dubLang.Count){ - Console.WriteLine("Added Episode to Queue but couldn't find all selected dubs"); - MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue but couldn't find all selected dubs", ToastType.Warning, 2)); - } else{ - Console.WriteLine("Added Episode to Queue"); - MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue", ToastType.Information, 1)); - } - } else{ - Console.WriteLine("Episode couldn't be added to Queue"); - MessageBus.Current.SendMessage(new ToastMessage($"Couldn't add episode to the queue with current dub settings", ToastType.Error, 2)); - } - } - } - - public async Task AddSeriesToQueue(CrunchySeriesList list, CrunchyMultiDownload data){ - var selected = CrSeries.ItemSelectMultiDub(list.Data, data.DubLang, data.But, data.AllEpisodes, data.E); - - bool failed = false; - - foreach (var crunchyEpMeta in selected.Values.ToList()){ - if (crunchyEpMeta.Data?.First().Playback != null){ - if (CrunOptions.History){ - var historyEpisode = CrHistory.GetHistoryEpisodeWithDownloadDir(crunchyEpMeta.ShowId, crunchyEpMeta.SeasonId, crunchyEpMeta.Data.First().MediaId); - if (CrunOptions.SonarrProperties is{ SonarrEnabled: true, UseSonarrNumbering: true }){ - if (historyEpisode.historyEpisode != null){ - if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrEpisodeNumber)){ - crunchyEpMeta.EpisodeNumber = historyEpisode.historyEpisode.SonarrEpisodeNumber; - } - - if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrSeasonNumber)){ - crunchyEpMeta.Season = historyEpisode.historyEpisode.SonarrSeasonNumber; - } - } - } - - if (!string.IsNullOrEmpty(historyEpisode.downloadDirPath)){ - crunchyEpMeta.DownloadPath = historyEpisode.downloadDirPath; - } - } - - if (CrunOptions.IncludeVideoDescription){ - if (crunchyEpMeta.Data is{ Count: > 0 }){ - var episode = await CrEpisode.ParseEpisodeById(crunchyEpMeta.Data.First().MediaId, string.IsNullOrEmpty(CrunOptions.DescriptionLang) ? DefaultLocale : CrunOptions.DescriptionLang, true); - crunchyEpMeta.Description = episode?.Description ?? crunchyEpMeta.Description; - } - } - - crunchyEpMeta.DownloadSubs = CrunOptions.DlSubs; - Queue.Add(crunchyEpMeta); - } else{ - failed = true; - } - } - - if (failed){ - MainWindow.Instance.ShowError("Not all episodes could be added – make sure that you are signed in with an account that has an active premium subscription?"); - } else{ - MessageBus.Current.SendMessage(new ToastMessage($"Added episodes to the queue", ToastType.Information, 1)); - } - } - - + public async Task DownloadEpisode(CrunchyEpMeta data, CrDownloadOptions options){ - ActiveDownloads++; + QueueManager.Instance.ActiveDownloads++; data.DownloadProgress = new DownloadProgress(){ IsDownloading = true, @@ -474,11 +182,11 @@ public class Crunchyroll{ DownloadSpeed = 0, Doing = "Starting" }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); var res = await DownloadMediaList(data, options); if (res.Error){ - ActiveDownloads--; + QueueManager.Instance.ActiveDownloads--; data.DownloadProgress = new DownloadProgress(){ IsDownloading = false, Error = true, @@ -487,7 +195,7 @@ public class Crunchyroll{ DownloadSpeed = 0, Doing = "Download Error" + (!string.IsNullOrEmpty(res.ErrorText) ? " - " + res.ErrorText : ""), }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); return false; } @@ -500,7 +208,7 @@ public class Crunchyroll{ Doing = "Muxing" }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); await MuxStreams(res.Data, new CrunchyMuxOptions{ @@ -532,17 +240,17 @@ public class Crunchyroll{ }; if (CrunOptions.RemoveFinishedDownload){ - Queue.Remove(data); + QueueManager.Instance.Queue.Remove(data); } } else{ Console.WriteLine("Skipping mux"); } - ActiveDownloads--; - Queue.Refresh(); + QueueManager.Instance.ActiveDownloads--; + QueueManager.Instance.Queue.Refresh(); if (CrunOptions.History && data.Data != null && data.Data.Count > 0){ - CrHistory.SetAsDownloaded(data.ShowId, data.SeasonId, data.Data.First().MediaId); + History.SetAsDownloaded(data.ShowId, data.SeasonId, data.Data.First().MediaId); } @@ -1190,7 +898,7 @@ public class Crunchyroll{ DownloadSpeed = 0, Doing = "Decrypting" }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); var assetIdRegexMatch = Regex.Match(chosenVideoSegments.segments[0].uri, @"/assets/(?:p/)?([^_,]+)"); var assetId = assetIdRegexMatch.Success ? assetIdRegexMatch.Groups[1].Value : null; @@ -1272,7 +980,7 @@ public class Crunchyroll{ DownloadSpeed = 0, Doing = "Decrypting video" }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); var decryptVideo = await Helpers.ExecuteCommandAsyncWorkDir("mp4decrypt", CfgManager.PathMP4Decrypt, commandVideo, tempTsFileWorkDir); if (!decryptVideo.IsOk){ @@ -1338,7 +1046,7 @@ public class Crunchyroll{ DownloadSpeed = 0, Doing = "Decrypting audio" }; - Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); var decryptAudio = await Helpers.ExecuteCommandAsyncWorkDir("mp4decrypt", CfgManager.PathMP4Decrypt, commandAudio, tempTsFileWorkDir); if (!decryptAudio.IsOk){ diff --git a/CRD/Downloader/History.cs b/CRD/Downloader/History.cs index 8e99e70..da49666 100644 --- a/CRD/Downloader/History.cs +++ b/CRD/Downloader/History.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Sonarr; using CRD.Utils.Sonarr.Models; @@ -16,9 +17,9 @@ using ReactiveUI; namespace CRD.Downloader; public class History(){ - private readonly Crunchyroll crunInstance = Crunchyroll.Instance; + private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; - public async Task UpdateSeries(string seriesId, string? seasonId){ + public async Task CRUpdateSeries(string seriesId, string? seasonId){ await crunInstance.CrAuth.RefreshToken(true); CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "ja-JP", true); @@ -266,11 +267,11 @@ public class History(){ if (seasonData.Data.First().Versions != null){ var version = seasonData.Data.First().Versions.Find(a => a.Original); if (version.AudioLocale != seasonData.Data.First().AudioLocale){ - UpdateSeries(seasonData.Data.First().SeriesId, version.SeasonGuid); + CRUpdateSeries(seasonData.Data.First().SeriesId, version.SeasonGuid); return; } } else{ - UpdateSeries(seasonData.Data.First().SeriesId, ""); + CRUpdateSeries(seasonData.Data.First().SeriesId, ""); return; } } @@ -380,18 +381,18 @@ public class History(){ } public void SortItems(){ - var currentSortingType = Crunchyroll.Instance.CrunOptions.HistoryPageProperties?.SelectedSorting ?? SortingType.SeriesTitle; - var sortingDir = Crunchyroll.Instance.CrunOptions.HistoryPageProperties != null && Crunchyroll.Instance.CrunOptions.HistoryPageProperties.Ascending; + var currentSortingType = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties?.SelectedSorting ?? SortingType.SeriesTitle; + var sortingDir = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null && CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending; DateTime today = DateTime.UtcNow.Date; switch (currentSortingType){ case SortingType.SeriesTitle: var sortedList = sortingDir - ? Crunchyroll.Instance.HistoryList.OrderByDescending(s => s.SeriesTitle).ToList() - : Crunchyroll.Instance.HistoryList.OrderBy(s => s.SeriesTitle).ToList(); + ? CrunchyrollManager.Instance.HistoryList.OrderByDescending(s => s.SeriesTitle).ToList() + : CrunchyrollManager.Instance.HistoryList.OrderBy(s => s.SeriesTitle).ToList(); - Crunchyroll.Instance.HistoryList.Clear(); + CrunchyrollManager.Instance.HistoryList.Clear(); - Crunchyroll.Instance.HistoryList.AddRange(sortedList); + CrunchyrollManager.Instance.HistoryList.AddRange(sortedList); return; @@ -399,7 +400,7 @@ public class History(){ case SortingType.NextAirDate: var sortedSeriesDates = sortingDir - ? Crunchyroll.Instance.HistoryList + ? CrunchyrollManager.Instance.HistoryList .OrderByDescending(s => { var date = ParseDate(s.SonarrNextAirDate, today); return date.HasValue ? date.Value : DateTime.MinValue; @@ -408,7 +409,7 @@ public class History(){ .ThenBy(s => string.IsNullOrEmpty(s.SonarrNextAirDate) ? 1 : 0) .ThenBy(s => s.SeriesTitle) .ToList() - : Crunchyroll.Instance.HistoryList + : CrunchyrollManager.Instance.HistoryList .OrderByDescending(s => s.SonarrNextAirDate == "Today") .ThenBy(s => s.SonarrNextAirDate == "Today" ? s.SeriesTitle : null) .ThenBy(s => { @@ -418,16 +419,16 @@ public class History(){ .ThenBy(s => s.SeriesTitle) .ToList(); - Crunchyroll.Instance.HistoryList.Clear(); + CrunchyrollManager.Instance.HistoryList.Clear(); - Crunchyroll.Instance.HistoryList.AddRange(sortedSeriesDates); + CrunchyrollManager.Instance.HistoryList.AddRange(sortedSeriesDates); return; case SortingType.HistorySeriesAddDate: - var sortedSeriesAddDates = Crunchyroll.Instance.HistoryList + var sortedSeriesAddDates = CrunchyrollManager.Instance.HistoryList .OrderBy(s => sortingDir ? -(s.HistorySeriesAddDate?.Date.Ticks ?? DateTime.MinValue.Ticks) : s.HistorySeriesAddDate?.Date.Ticks ?? DateTime.MaxValue.Ticks) @@ -435,9 +436,9 @@ public class History(){ .ToList(); - Crunchyroll.Instance.HistoryList.Clear(); + CrunchyrollManager.Instance.HistoryList.Clear(); - Crunchyroll.Instance.HistoryList.AddRange(sortedSeriesAddDates); + CrunchyrollManager.Instance.HistoryList.AddRange(sortedSeriesAddDates); return; } @@ -668,7 +669,7 @@ public class History(){ SonarrSeries? closestMatch = null; double highestSimilarity = 0.0; - Parallel.ForEach(crunInstance.SonarrSeries, series => { + Parallel.ForEach(SonarrClient.Instance.SonarrSeries, series => { double similarity = CalculateSimilarity(series.Title.ToLower(), title.ToLower()); if (similarity > highestSimilarity){ highestSimilarity = similarity; diff --git a/CRD/Downloader/QueueManager.cs b/CRD/Downloader/QueueManager.cs new file mode 100644 index 0000000..8d9413e --- /dev/null +++ b/CRD/Downloader/QueueManager.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Threading.Tasks; +using CRD.Downloader.Crunchyroll; +using CRD.Utils.CustomList; +using CRD.Utils.Structs; +using CRD.Utils.Structs.History; +using CRD.ViewModels; +using CRD.Views; +using ReactiveUI; + +namespace CRD.Downloader; + +public class QueueManager{ + #region Download Variables + + public RefreshableObservableCollection Queue = new RefreshableObservableCollection(); + public ObservableCollection DownloadItemModels = new ObservableCollection(); + public int ActiveDownloads; + + #endregion + + + #region Singelton + + private static QueueManager? _instance; + private static readonly object Padlock = new(); + + public static QueueManager Instance{ + get{ + if (_instance == null){ + lock (Padlock){ + if (_instance == null){ + _instance = new QueueManager(); + } + } + } + + return _instance; + } + } + + #endregion + + public QueueManager(){ + Queue.CollectionChanged += UpdateItemListOnRemove; + } + + + private void UpdateItemListOnRemove(object? sender, NotifyCollectionChangedEventArgs e){ + if (e.Action == NotifyCollectionChangedAction.Remove){ + if (e.OldItems != null) + foreach (var eOldItem in e.OldItems){ + var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(eOldItem)); + if (downloadItem != null){ + DownloadItemModels.Remove(downloadItem); + } else{ + Console.Error.WriteLine("Failed to Remove Episode from list"); + } + } + } + + UpdateDownloadListItems(); + } + + public void UpdateDownloadListItems(){ + var list = Queue; + + foreach (CrunchyEpMeta crunchyEpMeta in list){ + var downloadItem = DownloadItemModels.FirstOrDefault(e => e.epMeta.Equals(crunchyEpMeta)); + if (downloadItem != null){ + downloadItem.Refresh(); + } else{ + downloadItem = new DownloadItemModel(crunchyEpMeta); + downloadItem.LoadImage(); + DownloadItemModels.Add(downloadItem); + } + + if (downloadItem is{ isDownloading: false, Error: false } && CrunchyrollManager.Instance.CrunOptions.AutoDownload && ActiveDownloads < CrunchyrollManager.Instance.CrunOptions.SimultaneousDownloads){ + downloadItem.StartDownload(); + } + } + } + + + public async Task CRAddEpisodeToQue(string epId, string crLocale, List dubLang, bool updateHistory = false){ + await CrunchyrollManager.Instance.CrAuth.RefreshToken(true); + + var episodeL = await CrunchyrollManager.Instance.CrEpisode.ParseEpisodeById(epId, crLocale); + + + if (episodeL != null){ + if (episodeL.Value.IsPremiumOnly && !CrunchyrollManager.Instance.Profile.HasPremium){ + MessageBus.Current.SendMessage(new ToastMessage($"Episode is a premium episode – make sure that you are signed in with an account that has an active premium subscription", ToastType.Error, 3)); + return; + } + + var sList = await CrunchyrollManager.Instance.CrEpisode.EpisodeData((CrunchyEpisode)episodeL, updateHistory); + + (HistoryEpisode? historyEpisode, List dublist, string downloadDirPath) historyEpisode = (null, [], ""); + + if (CrunchyrollManager.Instance.CrunOptions.History){ + var episode = sList.EpisodeAndLanguages.Items.First(); + historyEpisode = CrunchyrollManager.Instance.History.GetHistoryEpisodeWithDubListAndDownloadDir(episode.SeriesId, episode.SeasonId, episode.Id); + if (historyEpisode.dublist.Count > 0){ + dubLang = historyEpisode.dublist; + } + } + + + var selected = CrunchyrollManager.Instance.CrEpisode.EpisodeMeta(sList, dubLang); + + if (CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription){ + if (selected.Data is{ Count: > 0 }){ + var episode = await CrunchyrollManager.Instance.CrEpisode.ParseEpisodeById(selected.Data.First().MediaId, + string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.DescriptionLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.DescriptionLang, true); + selected.Description = episode?.Description ?? selected.Description; + } + } + + if (selected.Data is{ Count: > 0 }){ + if (CrunchyrollManager.Instance.CrunOptions.History){ + // var historyEpisode = CrHistory.GetHistoryEpisodeWithDownloadDir(selected.ShowId, selected.SeasonId, selected.Data.First().MediaId); + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true, UseSonarrNumbering: true }){ + if (historyEpisode.historyEpisode != null){ + if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrEpisodeNumber)){ + selected.EpisodeNumber = historyEpisode.historyEpisode.SonarrEpisodeNumber; + } + + if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrSeasonNumber)){ + selected.Season = historyEpisode.historyEpisode.SonarrSeasonNumber; + } + } + } + + if (!string.IsNullOrEmpty(historyEpisode.downloadDirPath)){ + selected.DownloadPath = historyEpisode.downloadDirPath; + } + } + + selected.DownloadSubs = CrunchyrollManager.Instance.CrunOptions.DlSubs; + Queue.Add(selected); + + + if (selected.Data.Count < dubLang.Count){ + Console.WriteLine("Added Episode to Queue but couldn't find all selected dubs"); + MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue but couldn't find all selected dubs", ToastType.Warning, 2)); + } else{ + Console.WriteLine("Added Episode to Queue"); + MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue", ToastType.Information, 1)); + } + } else{ + Console.WriteLine("Episode couldn't be added to Queue"); + MessageBus.Current.SendMessage(new ToastMessage($"Couldn't add episode to the queue with current dub settings", ToastType.Error, 2)); + } + } + } + + public async Task CRAddSeriesToQueue(CrunchySeriesList list, CrunchyMultiDownload data){ + var selected = CrunchyrollManager.Instance.CrSeries.ItemSelectMultiDub(list.Data, data.DubLang, data.But, data.AllEpisodes, data.E); + + bool failed = false; + + foreach (var crunchyEpMeta in selected.Values.ToList()){ + if (crunchyEpMeta.Data?.First().Playback != null){ + if (CrunchyrollManager.Instance.CrunOptions.History){ + var historyEpisode = CrunchyrollManager.Instance.History.GetHistoryEpisodeWithDownloadDir(crunchyEpMeta.ShowId, crunchyEpMeta.SeasonId, crunchyEpMeta.Data.First().MediaId); + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true, UseSonarrNumbering: true }){ + if (historyEpisode.historyEpisode != null){ + if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrEpisodeNumber)){ + crunchyEpMeta.EpisodeNumber = historyEpisode.historyEpisode.SonarrEpisodeNumber; + } + + if (!string.IsNullOrEmpty(historyEpisode.historyEpisode.SonarrSeasonNumber)){ + crunchyEpMeta.Season = historyEpisode.historyEpisode.SonarrSeasonNumber; + } + } + } + + if (!string.IsNullOrEmpty(historyEpisode.downloadDirPath)){ + crunchyEpMeta.DownloadPath = historyEpisode.downloadDirPath; + } + } + + if (CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription){ + if (crunchyEpMeta.Data is{ Count: > 0 }){ + var episode = await CrunchyrollManager.Instance.CrEpisode.ParseEpisodeById(crunchyEpMeta.Data.First().MediaId, + string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.DescriptionLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.DescriptionLang, true); + crunchyEpMeta.Description = episode?.Description ?? crunchyEpMeta.Description; + } + } + + crunchyEpMeta.DownloadSubs = CrunchyrollManager.Instance.CrunOptions.DlSubs; + Queue.Add(crunchyEpMeta); + } else{ + failed = true; + } + } + + if (failed){ + MainWindow.Instance.ShowError("Not all episodes could be added – make sure that you are signed in with an account that has an active premium subscription?"); + } else{ + MessageBus.Current.SendMessage(new ToastMessage($"Added episodes to the queue", ToastType.Information, 1)); + } + } +} \ No newline at end of file diff --git a/CRD/Utils/Files/CfgManager.cs b/CRD/Utils/Files/CfgManager.cs index 3eb2bb8..12dd040 100644 --- a/CRD/Utils/Files/CfgManager.cs +++ b/CRD/Utils/Files/CfgManager.cs @@ -4,6 +4,7 @@ using System.IO; using System.IO.Compression; using System.Reflection; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Structs; using Newtonsoft.Json; using YamlDotNet.Core; @@ -139,7 +140,7 @@ public class CfgManager{ } } - var yaml = serializer.Serialize(Crunchyroll.Instance.CrunOptions); + var yaml = serializer.Serialize(CrunchyrollManager.Instance.CrunOptions); // Write to file File.WriteAllText(PathCrDownloadOptions, yaml); @@ -173,7 +174,7 @@ public class CfgManager{ var propertiesPresentInYaml = GetTopLevelPropertiesInYaml(input); var loadedOptions = deserializer.Deserialize(new StringReader(input)); - var instanceOptions = Crunchyroll.Instance.CrunOptions; + var instanceOptions = CrunchyrollManager.Instance.CrunOptions; foreach (PropertyInfo property in typeof(CrDownloadOptions).GetProperties()){ var yamlMemberAttribute = property.GetCustomAttribute(); @@ -207,7 +208,7 @@ public class CfgManager{ } public static void UpdateHistoryFile(){ - WriteJsonToFile(PathCrHistory, Crunchyroll.Instance.HistoryList); + WriteJsonToFile(PathCrHistory, CrunchyrollManager.Instance.HistoryList); } private static object fileLock = new object(); diff --git a/CRD/Utils/HLS/HLSDownloader.cs b/CRD/Utils/HLS/HLSDownloader.cs index c81a9f4..f7c0972 100644 --- a/CRD/Utils/HLS/HLSDownloader.cs +++ b/CRD/Utils/HLS/HLSDownloader.cs @@ -266,15 +266,15 @@ public class HlsDownloader{ Doing = _isAudio ? "Downloading Audio" : (_isVideo ? "Downloading Video" : "") }; - if (!Crunchyroll.Instance.Queue.Contains(_currentEpMeta)){ + if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){ return (Ok: false, _data.Parts); } - Crunchyroll.Instance.Queue.Refresh(); + QueueManager.Instance.Queue.Refresh(); while (_currentEpMeta.Paused){ await Task.Delay(500); - if (!Crunchyroll.Instance.Queue.Contains(_currentEpMeta)){ + if (!QueueManager.Instance.Queue.Contains(_currentEpMeta)){ return (Ok: false, _data.Parts); } } diff --git a/CRD/Utils/HLS/ThrottledStream.cs b/CRD/Utils/HLS/ThrottledStream.cs index a0a50ca..c0e57a4 100644 --- a/CRD/Utils/HLS/ThrottledStream.cs +++ b/CRD/Utils/HLS/ThrottledStream.cs @@ -1,4 +1,5 @@ using CRD.Downloader; +using CRD.Downloader.Crunchyroll; namespace CRD.Utils.HLS; @@ -31,11 +32,11 @@ public class GlobalThrottler{ public void Throttle(int bytesRead){ - if (Crunchyroll.Instance.CrunOptions.DownloadSpeedLimit == 0) return; + if (CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit == 0) return; lock (_lock){ _totalBytesRead += bytesRead; - if (_totalBytesRead >= ((Crunchyroll.Instance.CrunOptions.DownloadSpeedLimit * 1024) / 10)){ + if (_totalBytesRead >= ((CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit * 1024) / 10)){ var timeElapsed = DateTime.Now - _lastReadTime; if (timeElapsed.TotalMilliseconds < 100){ Thread.Sleep(100 - (int)timeElapsed.TotalMilliseconds); @@ -77,8 +78,8 @@ public class ThrottledStream : Stream{ public override int Read(byte[] buffer, int offset, int count){ int bytesRead = 0; - if (Crunchyroll.Instance.CrunOptions.DownloadSpeedLimit != 0){ - int bytesToRead = Math.Min(count, (Crunchyroll.Instance.CrunOptions.DownloadSpeedLimit * 1024) / 10); + if (CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit != 0){ + int bytesToRead = Math.Min(count, (CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit * 1024) / 10); bytesRead = _baseStream.Read(buffer, offset, bytesToRead); _throttler.Throttle(bytesRead); } else{ diff --git a/CRD/Utils/Http/HttpClientReq.cs b/CRD/Utils/Http/HttpClientReq.cs index a77266b..952c61c 100644 --- a/CRD/Utils/Http/HttpClientReq.cs +++ b/CRD/Utils/Http/HttpClientReq.cs @@ -6,6 +6,7 @@ using System.Collections.Specialized; using System.Net.Http.Headers; using System.Threading.Tasks; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; namespace CRD.Utils; @@ -93,7 +94,7 @@ public class HttpClientReq{ var request = new HttpRequestMessage(requestMethod, uriBuilder.ToString()); if (authHeader){ - request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", Crunchyroll.Instance.Token?.access_token); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", CrunchyrollManager.Instance.Token?.access_token); } if (disableDrmHeader){ diff --git a/CRD/Utils/Muxing/SyncingHelper.cs b/CRD/Utils/Muxing/SyncingHelper.cs index 9e0b79d..64f271f 100644 --- a/CRD/Utils/Muxing/SyncingHelper.cs +++ b/CRD/Utils/Muxing/SyncingHelper.cs @@ -135,7 +135,7 @@ public class SyncingHelper{ public static bool AreFramesSimilar(string imagePath1, string imagePath2, double ssimThreshold){ double ssim = ComputeSSIM(imagePath1, imagePath2, 256, 256); - Console.WriteLine($"SSIM: {ssim}"); + // Console.WriteLine($"SSIM: {ssim}"); return ssim > ssimThreshold; } diff --git a/CRD/Utils/Sonarr/SonarrClient.cs b/CRD/Utils/Sonarr/SonarrClient.cs index 913faca..6071c4a 100644 --- a/CRD/Utils/Sonarr/SonarrClient.cs +++ b/CRD/Utils/Sonarr/SonarrClient.cs @@ -9,6 +9,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Sonarr.Models; using CRD.Views; using Newtonsoft.Json; @@ -22,6 +23,8 @@ public class SonarrClient{ private SonarrProperties properties; + public List SonarrSeries =[]; + #region Singelton private static SonarrClient? _instance; @@ -47,8 +50,16 @@ public class SonarrClient{ httpClient = new HttpClient(); } + public async void RefreshSonarr(){ + await CheckSonarrSettings(); + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){ + SonarrSeries = await GetSeries(); + CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(true); + } + } + public void SetApiUrl(){ - if (Crunchyroll.Instance.CrunOptions.SonarrProperties != null) properties = Crunchyroll.Instance.CrunOptions.SonarrProperties; + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) properties = CrunchyrollManager.Instance.CrunOptions.SonarrProperties; if (properties != null ){ apiUrl = $"http{(properties.UseSsl ? "s" : "")}://{(!string.IsNullOrEmpty(properties.Host) ? properties.Host : "localhost")}:{properties.Port}{(properties.UrlBase ?? "")}/api"; @@ -59,10 +70,10 @@ public class SonarrClient{ SetApiUrl(); - if (Crunchyroll.Instance.CrunOptions.SonarrProperties != null){ - Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false; + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){ + CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false; } else{ - Crunchyroll.Instance.CrunOptions.SonarrProperties = new SonarrProperties(){SonarrEnabled = false}; + CrunchyrollManager.Instance.CrunOptions.SonarrProperties = new SonarrProperties(){SonarrEnabled = false}; return; } @@ -74,10 +85,10 @@ public class SonarrClient{ try{ response = await httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); - if (Crunchyroll.Instance.CrunOptions.SonarrProperties != null) Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled = true; + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = true; } catch (Exception ex){ Debug.WriteLine($"[ERROR] [SonarrClient.GetJson] Endpoint URL: '{apiUrl}', {ex}"); - if (Crunchyroll.Instance.CrunOptions.SonarrProperties != null) Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false; + if (CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null) CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled = false; } diff --git a/CRD/Utils/Structs/CalendarStructs.cs b/CRD/Utils/Structs/CalendarStructs.cs index 1a93b01..e2cfc8f 100644 --- a/CRD/Utils/Structs/CalendarStructs.cs +++ b/CRD/Utils/Structs/CalendarStructs.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Avalonia.Media.Imaging; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; namespace CRD.Utils.Structs; @@ -47,7 +48,7 @@ public partial class CalendarEpisode : INotifyPropertyChanged{ if (match.Success){ var locale = match.Groups[1].Value; // Capture the locale part var id = match.Groups[2].Value; // Capture the ID part - Crunchyroll.Instance.AddEpisodeToQue(id, Languages.Locale2language(locale).CrLocale, Crunchyroll.Instance.CrunOptions.DubLang,true); + QueueManager.Instance.CRAddEpisodeToQue(id, Languages.Locale2language(locale).CrLocale, CrunchyrollManager.Instance.CrunOptions.DubLang,true); } } diff --git a/CRD/Utils/Structs/History/HistoryEpisode.cs b/CRD/Utils/Structs/History/HistoryEpisode.cs index 6d35462..83996e2 100644 --- a/CRD/Utils/Structs/History/HistoryEpisode.cs +++ b/CRD/Utils/Structs/History/HistoryEpisode.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.Threading.Tasks; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using Newtonsoft.Json; namespace CRD.Utils.Structs.History; @@ -66,7 +67,7 @@ public class HistoryEpisode : INotifyPropertyChanged{ } public async Task DownloadEpisode(){ - await Crunchyroll.Instance.AddEpisodeToQue(EpisodeId, string.IsNullOrEmpty(Crunchyroll.Instance.CrunOptions.HistoryLang) ? Crunchyroll.Instance.DefaultLocale : Crunchyroll.Instance.CrunOptions.HistoryLang, - Crunchyroll.Instance.CrunOptions.DubLang); + await QueueManager.Instance.CRAddEpisodeToQue(EpisodeId, string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang, + CrunchyrollManager.Instance.CrunOptions.DubLang); } } \ No newline at end of file diff --git a/CRD/Utils/Structs/History/HistorySeries.cs b/CRD/Utils/Structs/History/HistorySeries.cs index 0f758f2..909917f 100644 --- a/CRD/Utils/Structs/History/HistorySeries.cs +++ b/CRD/Utils/Structs/History/HistorySeries.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Media.Imaging; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.CustomList; using Newtonsoft.Json; @@ -243,10 +244,10 @@ public class HistorySeries : INotifyPropertyChanged{ public async Task FetchData(string? seasonId){ FetchingData = true; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData))); - await Crunchyroll.Instance.CrHistory.UpdateSeries(SeriesId, seasonId); + await CrunchyrollManager.Instance.History.CRUpdateSeries(SeriesId, seasonId); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesTitle))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesDescription))); - Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(false, this); + CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, this); UpdateNewEpisodes(); FetchingData = false; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData))); @@ -262,7 +263,7 @@ public class HistorySeries : INotifyPropertyChanged{ } public void OpenSonarrPage(){ - var sonarrProp = Crunchyroll.Instance.CrunOptions.SonarrProperties; + var sonarrProp = CrunchyrollManager.Instance.CrunOptions.SonarrProperties; if (sonarrProp == null) return; diff --git a/CRD/Utils/UI/UiSonarrIdToVisibilityConverter.cs b/CRD/Utils/UI/UiSonarrIdToVisibilityConverter.cs index c0e3d4c..d62dca6 100644 --- a/CRD/Utils/UI/UiSonarrIdToVisibilityConverter.cs +++ b/CRD/Utils/UI/UiSonarrIdToVisibilityConverter.cs @@ -2,6 +2,7 @@ using System.Globalization; using Avalonia.Data.Converters; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using FluentAvalonia.UI.Controls; namespace CRD.Utils.UI; @@ -9,7 +10,7 @@ namespace CRD.Utils.UI; public class UiSonarrIdToVisibilityConverter : IValueConverter{ public object Convert(object value, Type targetType, object parameter, CultureInfo culture){ if (value is string stringValue){ - return Crunchyroll.Instance.CrunOptions.SonarrProperties != null && (stringValue.Length > 0 && Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled); + return CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null && (stringValue.Length > 0 && CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled); } return false; diff --git a/CRD/ViewModels/AccountPageViewModel.cs b/CRD/ViewModels/AccountPageViewModel.cs index 752564a..496650b 100644 --- a/CRD/ViewModels/AccountPageViewModel.cs +++ b/CRD/ViewModels/AccountPageViewModel.cs @@ -7,6 +7,7 @@ using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Structs; using CRD.Views.Utils; using FluentAvalonia.UI.Controls; @@ -45,28 +46,28 @@ public partial class AccountPageViewModel : ViewModelBase{ } public void UpdatetProfile(){ - ProfileName = Crunchyroll.Instance.Profile.Username; // Default or fetched user name - LoginLogoutText = Crunchyroll.Instance.Profile.Username == "???" ? "Login" : "Logout"; // Default state - LoadProfileImage("https://static.crunchyroll.com/assets/avatar/170x170/" + Crunchyroll.Instance.Profile.Avatar); + ProfileName = CrunchyrollManager.Instance.Profile.Username; // Default or fetched user name + LoginLogoutText = CrunchyrollManager.Instance.Profile.Username == "???" ? "Login" : "Logout"; // Default state + LoadProfileImage("https://static.crunchyroll.com/assets/avatar/170x170/" + CrunchyrollManager.Instance.Profile.Avatar); - if (Crunchyroll.Instance.Profile.Subscription != null && Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts != null){ - if (Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.Count >= 1){ - var sub = Crunchyroll.Instance.Profile.Subscription?.SubscriptionProducts.First(); + if (CrunchyrollManager.Instance.Profile.Subscription != null && CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts != null){ + if (CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts.Count >= 1){ + var sub = CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts.First(); if (sub != null){ IsCancelled = sub.IsCancelled; } - }else if (Crunchyroll.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.Count >= 1){ - var sub = Crunchyroll.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.First(); + }else if (CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.Count >= 1){ + var sub = CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.First(); if (sub != null){ IsCancelled = !sub.AutoRenew; } - }else if(Crunchyroll.Instance.Profile.Subscription?.NonrecurringSubscriptionProducts.Count >= 1){ + }else if(CrunchyrollManager.Instance.Profile.Subscription?.NonrecurringSubscriptionProducts.Count >= 1){ IsCancelled = true; } - if (Crunchyroll.Instance.Profile.Subscription?.NextRenewalDate != null){ - _targetTime = Crunchyroll.Instance.Profile.Subscription.NextRenewalDate; + if (CrunchyrollManager.Instance.Profile.Subscription?.NextRenewalDate != null){ + _targetTime = CrunchyrollManager.Instance.Profile.Subscription.NextRenewalDate; _timer = new DispatcherTimer{ Interval = TimeSpan.FromSeconds(1) }; @@ -100,7 +101,7 @@ public partial class AccountPageViewModel : ViewModelBase{ _ = await dialog.ShowAsync(); } else{ - await Crunchyroll.Instance.CrAuth.AuthAnonymous(); + await CrunchyrollManager.Instance.CrAuth.AuthAnonymous(); UpdatetProfile(); } } diff --git a/CRD/ViewModels/AddDownloadPageViewModel.cs b/CRD/ViewModels/AddDownloadPageViewModel.cs index 3a7552c..1cb5f2a 100644 --- a/CRD/ViewModels/AddDownloadPageViewModel.cs +++ b/CRD/ViewModels/AddDownloadPageViewModel.cs @@ -13,6 +13,7 @@ using Avalonia.Media.Imaging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Structs; using CRD.Views; @@ -75,7 +76,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{ } private async Task UpdateSearch(string value){ - var searchResults = await Crunchyroll.Instance.CrSeries.Search(value, Crunchyroll.Instance.CrunOptions.HistoryLang); + var searchResults = await CrunchyrollManager.Instance.CrSeries.Search(value, CrunchyrollManager.Instance.CrunOptions.HistoryLang); var searchItems = searchResults?.Data?.First().Items; SearchItems.Clear(); @@ -157,7 +158,7 @@ public partial class AddDownloadPageViewModel : ViewModelBase{ } if (currentSeriesList != null){ - await Crunchyroll.Instance.AddSeriesToQueue(currentSeriesList.Value, new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, AddAllEpisodes, false, selectedEpisodes)); + await QueueManager.Instance.CRAddSeriesToQueue(currentSeriesList.Value, new CrunchyMultiDownload(CrunchyrollManager.Instance.CrunOptions.DubLang, AddAllEpisodes, false, selectedEpisodes)); } @@ -186,10 +187,10 @@ public partial class AddDownloadPageViewModel : ViewModelBase{ if (match.Success){ var locale = match.Groups[1].Value; // Capture the locale part var id = match.Groups[2].Value; // Capture the ID part - Crunchyroll.Instance.AddEpisodeToQue(id, + QueueManager.Instance.CRAddEpisodeToQue(id, string.IsNullOrEmpty(locale) - ? string.IsNullOrEmpty(Crunchyroll.Instance.CrunOptions.HistoryLang) ? Crunchyroll.Instance.DefaultLocale : Crunchyroll.Instance.CrunOptions.HistoryLang - : Languages.Locale2language(locale).CrLocale, Crunchyroll.Instance.CrunOptions.DubLang, true); + ? string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang + : Languages.Locale2language(locale).CrLocale, CrunchyrollManager.Instance.CrunOptions.DubLang, true); UrlInput = ""; selectedEpisodes.Clear(); SelectedItems.Clear(); @@ -212,9 +213,9 @@ public partial class AddDownloadPageViewModel : ViewModelBase{ ButtonEnabled = false; ShowLoading = true; - var list = await Crunchyroll.Instance.CrSeries.ListSeriesId(id, string.IsNullOrEmpty(locale) - ? string.IsNullOrEmpty(Crunchyroll.Instance.CrunOptions.HistoryLang) ? Crunchyroll.Instance.DefaultLocale : Crunchyroll.Instance.CrunOptions.HistoryLang - : Languages.Locale2language(locale).CrLocale, new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, true)); + var list = await CrunchyrollManager.Instance.CrSeries.ListSeriesId(id, string.IsNullOrEmpty(locale) + ? string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang + : Languages.Locale2language(locale).CrLocale, new CrunchyMultiDownload(CrunchyrollManager.Instance.CrunOptions.DubLang, true)); ShowLoading = false; if (list != null){ currentSeriesList = list; @@ -291,9 +292,9 @@ public partial class AddDownloadPageViewModel : ViewModelBase{ SearchVisible = false; ButtonEnabled = false; ShowLoading = true; - var list = await Crunchyroll.Instance.CrSeries.ListSeriesId(value.Id, - string.IsNullOrEmpty(Crunchyroll.Instance.CrunOptions.HistoryLang) ? Crunchyroll.Instance.DefaultLocale : Crunchyroll.Instance.CrunOptions.HistoryLang, - new CrunchyMultiDownload(Crunchyroll.Instance.CrunOptions.DubLang, true)); + var list = await CrunchyrollManager.Instance.CrSeries.ListSeriesId(value.Id, + string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.HistoryLang) ? CrunchyrollManager.Instance.DefaultLocale : CrunchyrollManager.Instance.CrunOptions.HistoryLang, + new CrunchyMultiDownload(CrunchyrollManager.Instance.CrunOptions.DubLang, true)); ShowLoading = false; if (list != null){ currentSeriesList = list; diff --git a/CRD/ViewModels/CalendarPageViewModel.cs b/CRD/ViewModels/CalendarPageViewModel.cs index 5166f2c..e8ac7fb 100644 --- a/CRD/ViewModels/CalendarPageViewModel.cs +++ b/CRD/ViewModels/CalendarPageViewModel.cs @@ -6,6 +6,7 @@ using Avalonia.Controls; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Structs; using DynamicData; @@ -63,14 +64,14 @@ public partial class CalendarPageViewModel : ViewModelBase{ CalendarDubFilter.Add(new ComboBoxItem{ Content = languageItem.CrLocale }); } - CustomCalendar = Crunchyroll.Instance.CrunOptions.CustomCalendar; - HideDubs = Crunchyroll.Instance.CrunOptions.CalendarHideDubs; - FilterByAirDate = Crunchyroll.Instance.CrunOptions.CalendarFilterByAirDate; + CustomCalendar = CrunchyrollManager.Instance.CrunOptions.CustomCalendar; + HideDubs = CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs; + FilterByAirDate = CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate; - ComboBoxItem? dubfilter = CalendarDubFilter.FirstOrDefault(a => a.Content != null && (string)a.Content == Crunchyroll.Instance.CrunOptions.CalendarDubFilter) ?? null; + ComboBoxItem? dubfilter = CalendarDubFilter.FirstOrDefault(a => a.Content != null && (string)a.Content == CrunchyrollManager.Instance.CrunOptions.CalendarDubFilter) ?? null; CurrentCalendarDubFilter = dubfilter ?? CalendarDubFilter[0]; - CurrentCalendarLanguage = CalendarLanguage.FirstOrDefault(a => a.Content != null && (string)a.Content == Crunchyroll.Instance.CrunOptions.SelectedCalendarLanguage) ?? CalendarLanguage[0]; + CurrentCalendarLanguage = CalendarLanguage.FirstOrDefault(a => a.Content != null && (string)a.Content == CrunchyrollManager.Instance.CrunOptions.SelectedCalendarLanguage) ?? CalendarLanguage[0]; loading = false; LoadCalendar(GetThisWeeksMondayDate(), false); } @@ -98,16 +99,18 @@ public partial class CalendarPageViewModel : ViewModelBase{ } public async void LoadCalendar(string mondayDate, bool forceUpdate){ - if (CustomCalendar){ - BuildCustomCalendar(); - return; - } - ShowLoading = true; - CalendarWeek week = await Crunchyroll.Instance.GetCalendarForDate(mondayDate, forceUpdate); - if (currentWeek != null && currentWeek == week){ - ShowLoading = false; - return; + + CalendarWeek week; + + if (CustomCalendar){ + week = await CalendarManager.Instance.BuildCustomCalendar(forceUpdate); + } else{ + week = await CalendarManager.Instance.GetCalendarForDate(mondayDate, forceUpdate); + if (currentWeek != null && currentWeek == week){ + ShowLoading = false; + return; + } } currentWeek = week; @@ -115,17 +118,26 @@ public partial class CalendarPageViewModel : ViewModelBase{ CalendarDays.AddRange(week.CalendarDays); RaisePropertyChanged(nameof(CalendarDays)); ShowLoading = false; - - foreach (var calendarDay in CalendarDays){ - var episodesCopy = new List(calendarDay.CalendarEpisodes); - foreach (var calendarDayCalendarEpisode in episodesCopy){ - if (calendarDayCalendarEpisode.SeasonName != null && HideDubs && calendarDayCalendarEpisode.SeasonName.EndsWith("Dub)")){ - calendarDay.CalendarEpisodes.Remove(calendarDayCalendarEpisode); - continue; + if (CustomCalendar){ + foreach (var calendarDay in CalendarDays){ + foreach (var calendarDayCalendarEpisode in calendarDay.CalendarEpisodes){ + if (calendarDayCalendarEpisode.ImageBitmap == null){ + calendarDayCalendarEpisode.LoadImage(); + } } + } + } else{ + foreach (var calendarDay in CalendarDays){ + var episodesCopy = new List(calendarDay.CalendarEpisodes); + foreach (var calendarDayCalendarEpisode in episodesCopy){ + if (calendarDayCalendarEpisode.SeasonName != null && HideDubs && calendarDayCalendarEpisode.SeasonName.EndsWith("Dub)")){ + calendarDay.CalendarEpisodes.Remove(calendarDayCalendarEpisode); + continue; + } - if (calendarDayCalendarEpisode.ImageBitmap == null){ - calendarDayCalendarEpisode.LoadImage(); + if (calendarDayCalendarEpisode.ImageBitmap == null){ + calendarDayCalendarEpisode.LoadImage(); + } } } } @@ -148,11 +160,6 @@ public partial class CalendarPageViewModel : ViewModelBase{ return; } - if (CustomCalendar){ - BuildCustomCalendar(); - return; - } - string mondayDate; if (currentWeek is{ FirstDayOfWeekString: not null }){ @@ -205,7 +212,7 @@ public partial class CalendarPageViewModel : ViewModelBase{ } if (value?.Content != null){ - Crunchyroll.Instance.CrunOptions.SelectedCalendarLanguage = value.Content.ToString(); + CrunchyrollManager.Instance.CrunOptions.SelectedCalendarLanguage = value.Content.ToString(); Refresh(); CfgManager.WriteSettingsToFile(); } @@ -216,13 +223,10 @@ public partial class CalendarPageViewModel : ViewModelBase{ return; } - if (CustomCalendar){ - BuildCustomCalendar(); - } else{ - LoadCalendar(GetThisWeeksMondayDate(), true); - } + CrunchyrollManager.Instance.CrunOptions.CustomCalendar = value; - Crunchyroll.Instance.CrunOptions.CustomCalendar = value; + LoadCalendar(GetThisWeeksMondayDate(), true); + CfgManager.WriteSettingsToFile(); } @@ -231,16 +235,16 @@ public partial class CalendarPageViewModel : ViewModelBase{ return; } - Crunchyroll.Instance.CrunOptions.CalendarHideDubs = value; + CrunchyrollManager.Instance.CrunOptions.CalendarHideDubs = value; CfgManager.WriteSettingsToFile(); } - + partial void OnFilterByAirDateChanged(bool value){ if (loading){ return; } - Crunchyroll.Instance.CrunOptions.CalendarFilterByAirDate = value; + CrunchyrollManager.Instance.CrunOptions.CalendarFilterByAirDate = value; CfgManager.WriteSettingsToFile(); } @@ -250,89 +254,8 @@ public partial class CalendarPageViewModel : ViewModelBase{ } if (!string.IsNullOrEmpty(value?.Content + "")){ - Crunchyroll.Instance.CrunOptions.CalendarDubFilter = value?.Content + ""; + CrunchyrollManager.Instance.CrunOptions.CalendarDubFilter = value?.Content + ""; CfgManager.WriteSettingsToFile(); } } - - private async void BuildCustomCalendar(){ - ShowLoading = true; - - var newEpisodesBase = await Crunchyroll.Instance.CrEpisode.GetNewEpisodes(Crunchyroll.Instance.CrunOptions.HistoryLang, 200,true); - - CalendarWeek week = new CalendarWeek(); - week.CalendarDays = new List(); - - DateTime today = DateTime.Now; - - for (int i = 0; i < 7; i++){ - CalendarDay calDay = new CalendarDay(); - - calDay.CalendarEpisodes = new List(); - calDay.DateTime = today.AddDays(-i); - calDay.DayName = calDay.DateTime.Value.DayOfWeek.ToString(); - - week.CalendarDays.Add(calDay); - } - - week.CalendarDays.Reverse(); - - if (newEpisodesBase is{ Data.Count: > 0 }){ - var newEpisodes = newEpisodesBase.Data; - - foreach (var crBrowseEpisode in newEpisodes){ - var targetDate = FilterByAirDate ? crBrowseEpisode.EpisodeMetadata.EpisodeAirDate : crBrowseEpisode.LastPublic; - - if (HideDubs && crBrowseEpisode.EpisodeMetadata.SeasonTitle != null && (crBrowseEpisode.EpisodeMetadata.SeasonTitle.EndsWith("Dub)") || crBrowseEpisode.EpisodeMetadata.AudioLocale != Locale.JaJp)){ - continue; - } - - var dubFilter = CurrentCalendarDubFilter?.Content + ""; - if (!string.IsNullOrEmpty(dubFilter) && dubFilter != "none"){ - if (crBrowseEpisode.EpisodeMetadata.AudioLocale != null && crBrowseEpisode.EpisodeMetadata.AudioLocale.GetEnumMemberValue() != dubFilter){ - continue; - } - } - - var calendarDay = (from day in week.CalendarDays - where day.DateTime.HasValue && day.DateTime.Value.Date == targetDate.Date - select day).FirstOrDefault(); - - if (calendarDay != null){ - CalendarEpisode calEpisode = new CalendarEpisode(); - - calEpisode.DateTime = targetDate; - calEpisode.HasPassed = DateTime.Now > targetDate; - calEpisode.EpisodeName = crBrowseEpisode.Title; - calEpisode.SeriesUrl = $"https://www.crunchyroll.com/{Crunchyroll.Instance.CrunOptions.HistoryLang}/series/" + crBrowseEpisode.EpisodeMetadata.SeriesId; - calEpisode.EpisodeUrl = $"https://www.crunchyroll.com/{Crunchyroll.Instance.CrunOptions.HistoryLang}/watch/{crBrowseEpisode.Id}/"; - calEpisode.ThumbnailUrl = crBrowseEpisode.Images.Thumbnail.First().First().Source; - calEpisode.IsPremiumOnly = crBrowseEpisode.EpisodeMetadata.IsPremiumOnly; - calEpisode.IsPremiere = crBrowseEpisode.EpisodeMetadata.Episode == "1"; - calEpisode.SeasonName = crBrowseEpisode.EpisodeMetadata.SeasonTitle; - calEpisode.EpisodeNumber = crBrowseEpisode.EpisodeMetadata.Episode; - - calendarDay.CalendarEpisodes?.Add(calEpisode); - } - } - } - - - foreach (var day in week.CalendarDays){ - if (day.CalendarEpisodes != null) day.CalendarEpisodes = day.CalendarEpisodes.OrderBy(e => e.DateTime).ToList(); - } - - currentWeek = week; - CalendarDays.Clear(); - CalendarDays.AddRange(week.CalendarDays); - RaisePropertyChanged(nameof(CalendarDays)); - ShowLoading = false; - foreach (var calendarDay in CalendarDays){ - foreach (var calendarDayCalendarEpisode in calendarDay.CalendarEpisodes){ - if (calendarDayCalendarEpisode.ImageBitmap == null){ - calendarDayCalendarEpisode.LoadImage(); - } - } - } - } } \ No newline at end of file diff --git a/CRD/ViewModels/ContentDialogInputLoginViewModel.cs b/CRD/ViewModels/ContentDialogInputLoginViewModel.cs index 9ccabd7..e3ec828 100644 --- a/CRD/ViewModels/ContentDialogInputLoginViewModel.cs +++ b/CRD/ViewModels/ContentDialogInputLoginViewModel.cs @@ -1,6 +1,7 @@ using System; using CommunityToolkit.Mvvm.ComponentModel; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Structs; using FluentAvalonia.UI.Controls; @@ -31,7 +32,7 @@ public partial class ContentDialogInputLoginViewModel : ViewModelBase{ private async void LoginButton(ContentDialog sender, ContentDialogButtonClickEventArgs args){ dialog.PrimaryButtonClick -= LoginButton; - await Crunchyroll.Instance.CrAuth.Auth(new AuthData{Password = Password,Username = Email}); + await CrunchyrollManager.Instance.CrAuth.Auth(new AuthData{Password = Password,Username = Email}); accountPageViewModel.UpdatetProfile(); } diff --git a/CRD/ViewModels/DownloadsPageViewModel.cs b/CRD/ViewModels/DownloadsPageViewModel.cs index 982bcd9..fb46262 100644 --- a/CRD/ViewModels/DownloadsPageViewModel.cs +++ b/CRD/ViewModels/DownloadsPageViewModel.cs @@ -8,6 +8,7 @@ using Avalonia.Media.Imaging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Structs; namespace CRD.ViewModels; @@ -20,25 +21,24 @@ public partial class DownloadsPageViewModel : ViewModelBase{ [ObservableProperty] public bool _removeFinished; - + public DownloadsPageViewModel(){ - Crunchyroll.Instance.UpdateDownloadListItems(); - Items = Crunchyroll.Instance.DownloadItemModels; - AutoDownload = Crunchyroll.Instance.CrunOptions.AutoDownload; - RemoveFinished = Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload; + QueueManager.Instance.UpdateDownloadListItems(); + Items = QueueManager.Instance.DownloadItemModels; + AutoDownload = CrunchyrollManager.Instance.CrunOptions.AutoDownload; + RemoveFinished = CrunchyrollManager.Instance.CrunOptions.RemoveFinishedDownload; } partial void OnAutoDownloadChanged(bool value){ - Crunchyroll.Instance.CrunOptions.AutoDownload = value; + CrunchyrollManager.Instance.CrunOptions.AutoDownload = value; if (value){ - Crunchyroll.Instance.UpdateDownloadListItems(); + QueueManager.Instance.UpdateDownloadListItems(); } } partial void OnRemoveFinishedChanged(bool value){ - Crunchyroll.Instance.CrunOptions.RemoveFinishedDownload = value; + CrunchyrollManager.Instance.CrunOptions.RemoveFinishedDownload = value; } - } public partial class DownloadItemModel : INotifyPropertyChanged{ @@ -81,11 +81,6 @@ public partial class DownloadItemModel : INotifyPropertyChanged{ } private string GetDubString(){ - var hardSubs = Crunchyroll.Instance.CrunOptions.Hslang != "none" ? "Hardsub: " + Crunchyroll.Instance.CrunOptions.Hslang : ""; - if (hardSubs != string.Empty){ - return hardSubs; - } - var dubs = "Dub: "; if (epMeta.SelectedDubs != null) @@ -97,8 +92,13 @@ public partial class DownloadItemModel : INotifyPropertyChanged{ } private string GetSubtitleString(){ - var hardSubs = Crunchyroll.Instance.CrunOptions.Hslang != "none" ? "Hardsub: " + Crunchyroll.Instance.CrunOptions.Hslang : ""; + var hardSubs = CrunchyrollManager.Instance.CrunOptions.Hslang != "none" ? "Hardsub: " : ""; if (hardSubs != string.Empty){ + var locale = Languages.Locale2language(CrunchyrollManager.Instance.CrunOptions.Hslang).CrLocale; + if (epMeta.AvailableSubs != null && epMeta.AvailableSubs.Contains(locale)){ + hardSubs += locale + " "; + } + return hardSubs; } @@ -181,15 +181,15 @@ public partial class DownloadItemModel : INotifyPropertyChanged{ epMeta.DownloadProgress.IsDownloading = true; Paused = !epMeta.Paused && !isDownloading || epMeta.Paused; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Paused))); - await Crunchyroll.Instance.DownloadEpisode(epMeta, Crunchyroll.Instance.CrunOptions); + await CrunchyrollManager.Instance.DownloadEpisode(epMeta, CrunchyrollManager.Instance.CrunOptions); } } [RelayCommand] public void RemoveFromQueue(){ - CrunchyEpMeta? downloadItem = Crunchyroll.Instance.Queue.FirstOrDefault(e => e.Equals(epMeta)) ?? null; + CrunchyEpMeta? downloadItem = QueueManager.Instance.Queue.FirstOrDefault(e => e.Equals(epMeta)) ?? null; if (downloadItem != null){ - Crunchyroll.Instance.Queue.Remove(downloadItem); + QueueManager.Instance.Queue.Remove(downloadItem); } } diff --git a/CRD/ViewModels/HistoryPageViewModel.cs b/CRD/ViewModels/HistoryPageViewModel.cs index e529938..354aa4f 100644 --- a/CRD/ViewModels/HistoryPageViewModel.cs +++ b/CRD/ViewModels/HistoryPageViewModel.cs @@ -12,6 +12,7 @@ using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Structs; using CRD.Utils.Structs.History; @@ -86,9 +87,9 @@ public partial class HistoryPageViewModel : ViewModelBase{ public static bool _sortDir = false; public HistoryPageViewModel(){ - Items = Crunchyroll.Instance.HistoryList; + Items = CrunchyrollManager.Instance.HistoryList; - HistoryPageProperties? properties = Crunchyroll.Instance.CrunOptions.HistoryPageProperties; + HistoryPageProperties? properties = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties; currentViewType = properties?.SelectedView ?? HistoryViewType.Posters; currentSortingType = properties?.SelectedSorting ?? SortingType.SeriesTitle; @@ -123,17 +124,17 @@ public partial class HistoryPageViewModel : ViewModelBase{ historySeries.UpdateNewEpisodes(); } - Crunchyroll.Instance.CrHistory.SortItems(); + CrunchyrollManager.Instance.History.SortItems(); } private void UpdateSettings(){ - if (Crunchyroll.Instance.CrunOptions.HistoryPageProperties != null){ - Crunchyroll.Instance.CrunOptions.HistoryPageProperties.ScaleValue = ScaleValue; - Crunchyroll.Instance.CrunOptions.HistoryPageProperties.SelectedView = currentViewType; - Crunchyroll.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType; + if (CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null){ + CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.ScaleValue = ScaleValue; + CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.SelectedView = currentViewType; + CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType; } else{ - Crunchyroll.Instance.CrunOptions.HistoryPageProperties = new HistoryPageProperties(){ ScaleValue = ScaleValue, SelectedView = currentViewType, SelectedSorting = currentSortingType }; + CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties = new HistoryPageProperties(){ ScaleValue = ScaleValue, SelectedView = currentViewType, SelectedSorting = currentSortingType }; } CfgManager.WriteSettingsToFile(); @@ -155,9 +156,9 @@ public partial class HistoryPageViewModel : ViewModelBase{ partial void OnSelectedSortingChanged(SortingListElement? oldValue, SortingListElement? newValue){ if (newValue == null){ - if (Crunchyroll.Instance.CrunOptions.HistoryPageProperties != null){ - Crunchyroll.Instance.CrunOptions.HistoryPageProperties.Ascending = !Crunchyroll.Instance.CrunOptions.HistoryPageProperties.Ascending; - SortDir = Crunchyroll.Instance.CrunOptions.HistoryPageProperties.Ascending; + if (CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null){ + CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending = !CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending; + SortDir = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending; } Dispatcher.UIThread.InvokeAsync(() => { @@ -169,8 +170,8 @@ public partial class HistoryPageViewModel : ViewModelBase{ if (newValue.SelectedSorting != null){ currentSortingType = newValue.SelectedSorting; - if (Crunchyroll.Instance.CrunOptions.HistoryPageProperties != null) Crunchyroll.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType; - Crunchyroll.Instance.CrHistory.SortItems(); + if (CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null) CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.SelectedSorting = currentSortingType; + CrunchyrollManager.Instance.History.SortItems(); } else{ Console.Error.WriteLine("Invalid viewtype selected"); } @@ -211,12 +212,12 @@ public partial class HistoryPageViewModel : ViewModelBase{ partial void OnSelectedSeriesChanged(HistorySeries value){ - Crunchyroll.Instance.SelectedSeries = value; + CrunchyrollManager.Instance.SelectedSeries = value; NavToSeries(); - if (!string.IsNullOrEmpty(value.SonarrSeriesId) && Crunchyroll.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){ - Crunchyroll.Instance.CrHistory.MatchHistoryEpisodesWithSonarr(true, SelectedSeries); + if (!string.IsNullOrEmpty(value.SonarrSeriesId) && CrunchyrollManager.Instance.CrunOptions.SonarrProperties is{ SonarrEnabled: true }){ + CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(true, SelectedSeries); } @@ -225,9 +226,9 @@ public partial class HistoryPageViewModel : ViewModelBase{ [RelayCommand] public void RemoveSeries(string? seriesId){ - HistorySeries? objectToRemove = Crunchyroll.Instance.HistoryList.ToList().Find(se => se.SeriesId == seriesId) ?? null; + HistorySeries? objectToRemove = CrunchyrollManager.Instance.HistoryList.ToList().Find(se => se.SeriesId == seriesId) ?? null; if (objectToRemove != null){ - Crunchyroll.Instance.HistoryList.Remove(objectToRemove); + CrunchyrollManager.Instance.HistoryList.Remove(objectToRemove); Items.Remove(objectToRemove); CfgManager.UpdateHistoryFile(); } @@ -260,7 +261,7 @@ public partial class HistoryPageViewModel : ViewModelBase{ FetchingData = false; RaisePropertyChanged(nameof(FetchingData)); - Crunchyroll.Instance.CrHistory.SortItems(); + CrunchyrollManager.Instance.History.SortItems(); } [RelayCommand] diff --git a/CRD/ViewModels/MainWindowViewModel.cs b/CRD/ViewModels/MainWindowViewModel.cs index 0cb12b0..cc2e3d5 100644 --- a/CRD/ViewModels/MainWindowViewModel.cs +++ b/CRD/ViewModels/MainWindowViewModel.cs @@ -8,6 +8,7 @@ using Avalonia.Media; using Avalonia.Styling; using CommunityToolkit.Mvvm.ComponentModel; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Updater; using FluentAvalonia.Styling; using Newtonsoft.Json; @@ -48,15 +49,15 @@ public partial class MainWindowViewModel : ViewModelBase{ public async void Init(){ UpdateAvailable = await Updater.Instance.CheckForUpdatesAsync(); - Crunchyroll.Instance.InitOptions(); + CrunchyrollManager.Instance.InitOptions(); - if (Crunchyroll.Instance.CrunOptions.AccentColor != null){ - _faTheme.CustomAccentColor = Color.Parse(Crunchyroll.Instance.CrunOptions.AccentColor); + if (CrunchyrollManager.Instance.CrunOptions.AccentColor != null){ + _faTheme.CustomAccentColor = Color.Parse(CrunchyrollManager.Instance.CrunOptions.AccentColor); } - if (Crunchyroll.Instance.CrunOptions.Theme == "System"){ + if (CrunchyrollManager.Instance.CrunOptions.Theme == "System"){ _faTheme.PreferSystemTheme = true; - } else if (Crunchyroll.Instance.CrunOptions.Theme == "Dark"){ + } else if (CrunchyrollManager.Instance.CrunOptions.Theme == "Dark"){ _faTheme.PreferSystemTheme = false; Application.Current.RequestedThemeVariant = ThemeVariant.Dark; } else{ @@ -64,7 +65,7 @@ public partial class MainWindowViewModel : ViewModelBase{ Application.Current.RequestedThemeVariant = ThemeVariant.Light; } - await Crunchyroll.Instance.Init(); + await CrunchyrollManager.Instance.Init(); } } \ No newline at end of file diff --git a/CRD/ViewModels/SeriesPageViewModel.cs b/CRD/ViewModels/SeriesPageViewModel.cs index 8a8c9fe..e238731 100644 --- a/CRD/ViewModels/SeriesPageViewModel.cs +++ b/CRD/ViewModels/SeriesPageViewModel.cs @@ -8,6 +8,7 @@ using Avalonia.Platform.Storage; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.Sonarr; using CRD.Utils.Structs; @@ -37,14 +38,14 @@ public partial class SeriesPageViewModel : ViewModelBase{ - _selectedSeries = Crunchyroll.Instance.SelectedSeries; + _selectedSeries = CrunchyrollManager.Instance.SelectedSeries; if (_selectedSeries.ThumbnailImage == null){ _selectedSeries.LoadImage(); } - if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId) && Crunchyroll.Instance.CrunOptions.SonarrProperties != null){ - SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && Crunchyroll.Instance.CrunOptions.SonarrProperties.SonarrEnabled; + if (!string.IsNullOrEmpty(SelectedSeries.SonarrSeriesId) && CrunchyrollManager.Instance.CrunOptions.SonarrProperties != null){ + SonarrAvailable = SelectedSeries.SonarrSeriesId.Length > 0 && CrunchyrollManager.Instance.CrunOptions.SonarrProperties.SonarrEnabled; } else{ SonarrAvailable = false; } diff --git a/CRD/ViewModels/SettingsPageViewModel.cs b/CRD/ViewModels/SettingsPageViewModel.cs index c046544..327e667 100644 --- a/CRD/ViewModels/SettingsPageViewModel.cs +++ b/CRD/ViewModels/SettingsPageViewModel.cs @@ -15,6 +15,7 @@ using Avalonia.Styling; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils; using CRD.Utils.CustomList; using CRD.Utils.Sonarr; @@ -327,7 +328,7 @@ public partial class SettingsPageViewModel : ViewModelBase{ DefaultSubLangList.Add(new ComboBoxItem{ Content = languageItem.CrLocale }); } - CrDownloadOptions options = Crunchyroll.Instance.CrunOptions; + CrDownloadOptions options = CrunchyrollManager.Instance.CrunOptions; DownloadDirPath = string.IsNullOrEmpty(options.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : options.DownloadDirPath; @@ -337,7 +338,7 @@ public partial class SettingsPageViewModel : ViewModelBase{ ComboBoxItem? historyLang = HistoryLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.HistoryLang) ?? null; SelectedHistoryLang = historyLang ?? HistoryLangList[0]; - ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.Hslang) ?? null; + ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == Languages.Locale2language(options.Hslang).CrLocale) ?? null; SelectedHSLang = hsLang ?? HardSubLangList[0]; ComboBoxItem? defaultDubLang = DefaultDubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.DefaultAudio ?? "")) ?? null; @@ -441,62 +442,62 @@ public partial class SettingsPageViewModel : ViewModelBase{ return; } - Crunchyroll.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription; - Crunchyroll.Instance.CrunOptions.VideoTitle = FileTitle; - Crunchyroll.Instance.CrunOptions.Novids = !DownloadVideo; - Crunchyroll.Instance.CrunOptions.Noaudio = !DownloadAudio; - Crunchyroll.Instance.CrunOptions.DlVideoOnce = !DownloadVideoForEveryDub; - Crunchyroll.Instance.CrunOptions.Chapters = DownloadChapters; - Crunchyroll.Instance.CrunOptions.Mp4 = MuxToMp4; - Crunchyroll.Instance.CrunOptions.SyncTiming = SyncTimings; - Crunchyroll.Instance.CrunOptions.SkipSubsMux = SkipSubMux; - Crunchyroll.Instance.CrunOptions.Numbers = Math.Clamp((int)(LeadingNumbers ?? 0),0,10); - Crunchyroll.Instance.CrunOptions.FileName = FileName; - Crunchyroll.Instance.CrunOptions.IncludeSignsSubs = IncludeSignSubs; - Crunchyroll.Instance.CrunOptions.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0),0,1000000000); - Crunchyroll.Instance.CrunOptions.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0),1,10); + CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription; + CrunchyrollManager.Instance.CrunOptions.VideoTitle = FileTitle; + CrunchyrollManager.Instance.CrunOptions.Novids = !DownloadVideo; + CrunchyrollManager.Instance.CrunOptions.Noaudio = !DownloadAudio; + CrunchyrollManager.Instance.CrunOptions.DlVideoOnce = !DownloadVideoForEveryDub; + CrunchyrollManager.Instance.CrunOptions.Chapters = DownloadChapters; + CrunchyrollManager.Instance.CrunOptions.Mp4 = MuxToMp4; + CrunchyrollManager.Instance.CrunOptions.SyncTiming = SyncTimings; + CrunchyrollManager.Instance.CrunOptions.SkipSubsMux = SkipSubMux; + CrunchyrollManager.Instance.CrunOptions.Numbers = Math.Clamp((int)(LeadingNumbers ?? 0),0,10); + CrunchyrollManager.Instance.CrunOptions.FileName = FileName; + CrunchyrollManager.Instance.CrunOptions.IncludeSignsSubs = IncludeSignSubs; + CrunchyrollManager.Instance.CrunOptions.DownloadSpeedLimit = Math.Clamp((int)(DownloadSpeed ?? 0),0,1000000000); + CrunchyrollManager.Instance.CrunOptions.SimultaneousDownloads = Math.Clamp((int)(SimultaneousDownloads ?? 0),1,10); - Crunchyroll.Instance.CrunOptions.SubsAddScaledBorder = GetScaledBorderAndShadowSelection(); + CrunchyrollManager.Instance.CrunOptions.SubsAddScaledBorder = GetScaledBorderAndShadowSelection(); List softSubs = new List(); foreach (var listBoxItem in SelectedSubLang){ softSubs.Add(listBoxItem.Content + ""); } - Crunchyroll.Instance.CrunOptions.DlSubs = softSubs; + CrunchyrollManager.Instance.CrunOptions.DlSubs = softSubs; string descLang = SelectedDescriptionLang.Content + ""; - Crunchyroll.Instance.CrunOptions.DescriptionLang = descLang != "default" ? descLang : Crunchyroll.Instance.DefaultLocale; + CrunchyrollManager.Instance.CrunOptions.DescriptionLang = descLang != "default" ? descLang : CrunchyrollManager.Instance.DefaultLocale; string historyLang = SelectedHistoryLang.Content + ""; - Crunchyroll.Instance.CrunOptions.HistoryLang = historyLang != "default" ? historyLang : Crunchyroll.Instance.DefaultLocale; + CrunchyrollManager.Instance.CrunOptions.HistoryLang = historyLang != "default" ? historyLang : CrunchyrollManager.Instance.DefaultLocale; string hslang = SelectedHSLang.Content + ""; - Crunchyroll.Instance.CrunOptions.Hslang = hslang != "none" ? Languages.FindLang(hslang).Locale : hslang; + CrunchyrollManager.Instance.CrunOptions.Hslang = hslang != "none" ? Languages.FindLang(hslang).Locale : hslang; - Crunchyroll.Instance.CrunOptions.DefaultAudio = SelectedDefaultDubLang.Content + ""; - Crunchyroll.Instance.CrunOptions.DefaultSub = SelectedDefaultSubLang.Content + ""; + CrunchyrollManager.Instance.CrunOptions.DefaultAudio = SelectedDefaultDubLang.Content + ""; + CrunchyrollManager.Instance.CrunOptions.DefaultSub = SelectedDefaultSubLang.Content + ""; - Crunchyroll.Instance.CrunOptions.StreamEndpoint = SelectedStreamEndpoint.Content + ""; + CrunchyrollManager.Instance.CrunOptions.StreamEndpoint = SelectedStreamEndpoint.Content + ""; List dubLangs = new List(); foreach (var listBoxItem in SelectedDubLang){ dubLangs.Add(listBoxItem.Content + ""); } - Crunchyroll.Instance.CrunOptions.DubLang = dubLangs; + CrunchyrollManager.Instance.CrunOptions.DubLang = dubLangs; - Crunchyroll.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + ""; - Crunchyroll.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + ""; - Crunchyroll.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + ""; + CrunchyrollManager.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + ""; + CrunchyrollManager.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + ""; + CrunchyrollManager.Instance.CrunOptions.Theme = CurrentAppTheme?.Content + ""; - Crunchyroll.Instance.CrunOptions.AccentColor = _faTheme.CustomAccentColor.ToString(); + CrunchyrollManager.Instance.CrunOptions.AccentColor = _faTheme.CustomAccentColor.ToString(); - Crunchyroll.Instance.CrunOptions.History = History; + CrunchyrollManager.Instance.CrunOptions.History = History; var props = new SonarrProperties(); @@ -513,23 +514,23 @@ public partial class SettingsPageViewModel : ViewModelBase{ props.ApiKey = SonarrApiKey; - Crunchyroll.Instance.CrunOptions.SonarrProperties = props; + CrunchyrollManager.Instance.CrunOptions.SonarrProperties = props; - Crunchyroll.Instance.CrunOptions.LogMode = LogMode; + CrunchyrollManager.Instance.CrunOptions.LogMode = LogMode; List mkvmergeParams = new List(); foreach (var mkvmergeParam in MkvMergeOptions){ mkvmergeParams.Add(mkvmergeParam.ParamValue); } - Crunchyroll.Instance.CrunOptions.MkvmergeOptions = mkvmergeParams; + CrunchyrollManager.Instance.CrunOptions.MkvmergeOptions = mkvmergeParams; List ffmpegParams = new List(); foreach (var ffmpegParam in FfmpegOptions){ ffmpegParams.Add(ffmpegParam.ParamValue); } - Crunchyroll.Instance.CrunOptions.FfmpegOptions = ffmpegParams; + CrunchyrollManager.Instance.CrunOptions.FfmpegOptions = ffmpegParams; CfgManager.WriteSettingsToFile(); } @@ -604,8 +605,8 @@ public partial class SettingsPageViewModel : ViewModelBase{ var selectedFolder = result[0]; // Do something with the selected folder path Console.WriteLine($"Selected folder: {selectedFolder.Path.LocalPath}"); - Crunchyroll.Instance.CrunOptions.DownloadDirPath = selectedFolder.Path.LocalPath; - DownloadDirPath = string.IsNullOrEmpty(Crunchyroll.Instance.CrunOptions.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : Crunchyroll.Instance.CrunOptions.DownloadDirPath; + CrunchyrollManager.Instance.CrunOptions.DownloadDirPath = selectedFolder.Path.LocalPath; + DownloadDirPath = string.IsNullOrEmpty(CrunchyrollManager.Instance.CrunOptions.DownloadDirPath) ? CfgManager.PathVIDEOS_DIR : CrunchyrollManager.Instance.CrunOptions.DownloadDirPath; CfgManager.WriteSettingsToFile(); } } diff --git a/CRD/Views/SettingsPageView.axaml.cs b/CRD/Views/SettingsPageView.axaml.cs index 94a4033..b59f71d 100644 --- a/CRD/Views/SettingsPageView.axaml.cs +++ b/CRD/Views/SettingsPageView.axaml.cs @@ -4,6 +4,7 @@ using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Markup.Xaml; using CRD.Downloader; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Sonarr; using CRD.ViewModels; @@ -16,7 +17,7 @@ public partial class SettingsPageView : UserControl{ private void OnUnloaded(object? sender, RoutedEventArgs e){ if (DataContext is SettingsPageViewModel viewModel){ - Crunchyroll.Instance.RefreshSonarr(); + SonarrClient.Instance.RefreshSonarr(); } } } \ No newline at end of file diff --git a/images/Settings_Download.png b/images/Settings_Download.png index 0fb1991..5b550a5 100644 --- a/images/Settings_Download.png +++ b/images/Settings_Download.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7835908cf3cdbf8cdfb33b914d4eb28916359fe89132c72ca3728f4f6e1c00b -size 26332 +oid sha256:e616fc7834bbcf6c56692a4e48edd61b24ab25bf0336fd48bf5f0068947d7812 +size 30526