From 9c6ad2d7e8775172f9b62ab9843ff9d6d70e49ea Mon Sep 17 00:00:00 2001 From: Elwador <75888166+Elwador@users.noreply.github.com> Date: Fri, 16 Aug 2024 14:21:23 +0200 Subject: [PATCH] Add - Downloader remembers the Window Size and Position https://github.com/Crunchy-DL/Crunchy-Downloader/discussions/78 Chg - Adjusted the subscription check if the end date has passed but crunchyroll still thinks it's active https://github.com/Crunchy-DL/Crunchy-Downloader/issues/80 --- CRD/Downloader/Crunchyroll/CRAuth.cs | 34 ++-- CRD/Downloader/Crunchyroll/CrEpisode.cs | 2 +- CRD/Downloader/Crunchyroll/CrSeries.cs | 47 ++--- CRD/Downloader/History.cs | 211 +++++++-------------- CRD/Utils/Files/CfgManager.cs | 1 + CRD/Utils/Structs/History/HistorySeries.cs | 2 +- CRD/Utils/Structs/Structs.cs | 30 ++- CRD/ViewModels/AccountPageViewModel.cs | 48 +++-- CRD/Views/MainWindow.axaml.cs | 78 ++++++-- 9 files changed, 229 insertions(+), 224 deletions(-) diff --git a/CRD/Downloader/Crunchyroll/CRAuth.cs b/CRD/Downloader/Crunchyroll/CRAuth.cs index 51ea53a..64c3c72 100644 --- a/CRD/Downloader/Crunchyroll/CRAuth.cs +++ b/CRD/Downloader/Crunchyroll/CRAuth.cs @@ -44,7 +44,6 @@ public class CrAuth{ PreferredContentAudioLanguage = "ja-JP", PreferredContentSubtitleLanguage = "de-DE" }; - } private void JsonTokenToFileAndVariable(string content){ @@ -52,7 +51,7 @@ public class CrAuth{ if (crunInstance.Token != null && crunInstance.Token.expires_in != null){ - crunInstance.Token.expires = DateTime.Now.AddMilliseconds((double)crunInstance.Token.expires_in); + crunInstance.Token.expires = DateTime.Now.AddSeconds((double)crunInstance.Token.expires_in); CfgManager.WriteTokenToYamlFile(crunInstance.Token, CfgManager.PathCrToken); } @@ -82,7 +81,8 @@ public class CrAuth{ if (response.ResponseContent.Contains("invalid_credentials")){ MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - because of invalid login credentials", ToastType.Error, 10)); } else{ - MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - {response.ResponseContent.Substring(0,response.ResponseContent.Length < 200 ? response.ResponseContent.Length : 200)}", ToastType.Error, 10)); + MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - {response.ResponseContent.Substring(0, response.ResponseContent.Length < 200 ? response.ResponseContent.Length : 200)}", + ToastType.Error, 10)); } } @@ -119,20 +119,27 @@ public class CrAuth{ if (subsc is{ SubscriptionProducts:{ Count: 0 }, ThirdPartySubscriptionProducts.Count: > 0 }){ var thirdPartySub = subsc.ThirdPartySubscriptionProducts.First(); var expiration = thirdPartySub.InGrace ? thirdPartySub.InGraceExpirationDate : thirdPartySub.ExpirationDate; - var remaining = expiration - DateTime.UtcNow; - crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; - crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; - crunInstance.Profile.Subscription.NextRenewalDate = expiration; + var remaining = expiration - DateTime.Now; + crunInstance.Profile.HasPremium = true; + if (crunInstance.Profile.Subscription != null){ + crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; + crunInstance.Profile.Subscription.NextRenewalDate = expiration; + } } else if (subsc is{ SubscriptionProducts:{ Count: 0 }, NonrecurringSubscriptionProducts.Count: > 0 }){ var nonRecurringSub = subsc.NonrecurringSubscriptionProducts.First(); - var remaining = nonRecurringSub.EndDate - DateTime.UtcNow; - crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; - crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; - crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate; + var remaining = nonRecurringSub.EndDate - DateTime.Now; + crunInstance.Profile.HasPremium = true; + if (crunInstance.Profile.Subscription != null){ + crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; + crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate; + } } else if (subsc is{ SubscriptionProducts:{ Count: 0 }, FunimationSubscriptions.Count: > 0 }){ crunInstance.Profile.HasPremium = true; + } else if (subsc is{ SubscriptionProducts.Count: > 0 }){ + crunInstance.Profile.HasPremium = true; } else{ - crunInstance.Profile.HasPremium = subsc.IsActive; + crunInstance.Profile.HasPremium = false; + Console.Error.WriteLine($"No subscription available:\n {JsonConvert.SerializeObject(subsc, Formatting.Indented)} "); } } else{ crunInstance.Profile.HasPremium = false; @@ -175,7 +182,6 @@ public class CrAuth{ await GetProfile(); } - } public async Task RefreshToken(bool needsToken){ @@ -213,7 +219,5 @@ public class CrAuth{ } else{ Console.Error.WriteLine("Refresh Token Auth Failed"); } - } - } \ No newline at end of file diff --git a/CRD/Downloader/Crunchyroll/CrEpisode.cs b/CRD/Downloader/Crunchyroll/CrEpisode.cs index 53a58ec..1c0b820 100644 --- a/CRD/Downloader/Crunchyroll/CrEpisode.cs +++ b/CRD/Downloader/Crunchyroll/CrEpisode.cs @@ -59,7 +59,7 @@ public class CrEpisode(){ CrunchyRollEpisodeData episode = new CrunchyRollEpisodeData(); if (crunInstance.CrunOptions.History && updateHistory){ - await crunInstance.History.UpdateWithEpisode(dlEpisode); + await crunInstance.History.UpdateWithSeasonData(new List(){dlEpisode}); var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == dlEpisode.SeriesId); if (historySeries != null){ CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(false); diff --git a/CRD/Downloader/Crunchyroll/CrSeries.cs b/CRD/Downloader/Crunchyroll/CrSeries.cs index 74f8916..62cb003 100644 --- a/CRD/Downloader/Crunchyroll/CrSeries.cs +++ b/CRD/Downloader/Crunchyroll/CrSeries.cs @@ -16,26 +16,6 @@ namespace CRD.Downloader.Crunchyroll; public class CrSeries(){ private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; - public async Task> DownloadFromSeriesId(string id, CrunchyMultiDownload data){ - var series = await ListSeriesId(id, "", data); - - if (series != null){ - var selected = ItemSelectMultiDub(series.Value.Data, data.DubLang, data.But, data.AllEpisodes, data.E); - - foreach (var crunchyEpMeta in selected.Values){ - if (crunchyEpMeta.Data == null) continue; - var languages = crunchyEpMeta.Data.Select((a) => - $" {a.Lang?.Name ?? "Unknown Language"}"); - - Console.WriteLine($"[S{crunchyEpMeta.Season}E{crunchyEpMeta.EpisodeNumber} - {crunchyEpMeta.EpisodeTitle} [{string.Join(", ", languages)}]"); - } - - return selected.Values.ToList(); - } - - return new List(); - } - public Dictionary ItemSelectMultiDub(Dictionary eps, List dubLang, bool? but, bool? all, List? e){ var ret = new Dictionary(); @@ -143,25 +123,31 @@ public class CrSeries(){ CrSeriesSearch? parsedSeries = await ParseSeriesById(id, crLocale); // one piece - GRMG8ZQZR - if (parsedSeries == null){ + if (parsedSeries == null || parsedSeries.Data == null){ Console.Error.WriteLine("Parse Data Invalid"); return null; } - var result = ParseSeriesResult(parsedSeries); + // var result = ParseSeriesResult(parsedSeries); Dictionary episodes = new Dictionary(); + + if (crunInstance.CrunOptions.History){ + crunInstance.History.CRUpdateSeries(id,""); + } - - foreach (int season in result.Keys){ - foreach (var key in result[season].Keys){ - var s = result[season][key]; + var cachedSeasonID = ""; + var seasonData = new CrunchyEpisodeList(); + + foreach (var s in parsedSeries.Data){ if (data?.S != null && s.Id != data.Value.S) continue; int fallbackIndex = 0; - var seasonData = await GetSeasonDataById(s.Id, ""); + if (cachedSeasonID != s.Id){ + seasonData = await GetSeasonDataById(s.Id, ""); + cachedSeasonID = s.Id; + } + if (seasonData.Data != null){ - if (crunInstance.CrunOptions.History){ - crunInstance.History.UpdateWithSeasonData(seasonData, false); - } + foreach (var episode in seasonData.Data){ // Prepare the episode array @@ -204,7 +190,6 @@ public class CrSeries(){ } } } - } } if (crunInstance.CrunOptions.History){ diff --git a/CRD/Downloader/History.cs b/CRD/Downloader/History.cs index e69f0bb..859e200 100644 --- a/CRD/Downloader/History.cs +++ b/CRD/Downloader/History.cs @@ -22,45 +22,40 @@ public class History(){ public async Task CRUpdateSeries(string seriesId, string? seasonId){ await crunInstance.CrAuth.RefreshToken(true); - CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "ja-JP", true); + CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "en-US", true); if (parsedSeries == null){ Console.Error.WriteLine("Parse Data Invalid"); return; } - var result = crunInstance.CrSeries.ParseSeriesResult(parsedSeries); - Dictionary episodes = new Dictionary(); - - foreach (int season in result.Keys){ - foreach (var key in result[season].Keys){ - var s = result[season][key]; + if (parsedSeries.Data != null){ + foreach (var s in parsedSeries.Data){ if (!string.IsNullOrEmpty(seasonId) && s.Id != seasonId) continue; var sId = s.Id; if (s.Versions is{ Count: > 0 }){ - foreach (var sVersion in s.Versions){ - if (sVersion.Original == true){ - if (sVersion.Guid != null){ - sId = sVersion.Guid; - } - - break; + foreach (var sVersion in s.Versions.Where(sVersion => sVersion.Original == true)){ + if (sVersion.Guid != null){ + sId = sVersion.Guid; } + + break; } } var seasonData = await crunInstance.CrSeries.GetSeasonDataById(sId, string.IsNullOrEmpty(crunInstance.CrunOptions.HistoryLang) ? crunInstance.DefaultLocale : crunInstance.CrunOptions.HistoryLang, true); - await UpdateWithSeasonData(seasonData); + if (seasonData.Data != null) await UpdateWithSeasonData(seasonData.Data); } - } - var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); - if (historySeries != null){ - MatchHistorySeriesWithSonarr(false); - await MatchHistoryEpisodesWithSonarr(false, historySeries); - CfgManager.UpdateHistoryFile(); + var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); + + if (historySeries != null){ + MatchHistorySeriesWithSonarr(false); + await MatchHistoryEpisodesWithSonarr(false, historySeries); + CfgManager.UpdateHistoryFile(); + } } } @@ -212,116 +207,43 @@ public class History(){ } - public async Task UpdateWithEpisode(CrunchyEpisode episodeParam){ - var episode = episodeParam; - - if (episode.Versions != null){ - var version = episode.Versions.Find(a => a.Original); - if (version.AudioLocale != episode.AudioLocale){ - var crEpisode = await crunInstance.CrEpisode.ParseEpisodeById(version.Guid, ""); - if (crEpisode != null){ - episode = crEpisode.Value; - } else{ - MessageBus.Current.SendMessage(new ToastMessage($"Couldn't update download History", ToastType.Warning, 1)); - return; - } - } - } - - - var seriesId = episode.SeriesId; - var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); - if (historySeries != null){ - historySeries.HistorySeriesAddDate ??= DateTime.Now; - - var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == episode.SeasonId); - - await RefreshSeriesData(seriesId, historySeries); - - if (historySeason != null){ - historySeason.SeasonTitle = episode.SeasonTitle; - historySeason.SeasonNum = Helpers.ExtractNumberAfterS(episode.Identifier) ?? episode.SeasonNumber + ""; - historySeason.SpecialSeason = CheckStringForSpecial(episode.Identifier); - if (historySeason.EpisodesList.All(e => e.EpisodeId != episode.Id)){ - var newHistoryEpisode = new HistoryEpisode{ - EpisodeTitle = episode.Identifier.Contains("|M|") ? episode.SeasonTitle : episode.Title, - EpisodeDescription = episode.Description, - EpisodeId = episode.Id, - Episode = episode.Episode, - EpisodeSeasonNum = Helpers.ExtractNumberAfterS(episode.Identifier) ?? episode.SeasonNumber + "", - SpecialEpisode = !int.TryParse(episode.Episode, out _), - }; - - historySeason.EpisodesList.Add(newHistoryEpisode); - - historySeason.EpisodesList.Sort(new NumericStringPropertyComparer()); - } - } else{ - var newSeason = NewHistorySeason(episode); - - historySeries.Seasons.Add(newSeason); - newSeason.Init(); - } - - historySeries.UpdateNewEpisodes(); - } else{ - historySeries = new HistorySeries{ - SeriesTitle = episode.SeriesTitle, - SeriesId = episode.SeriesId, - Seasons =[], - HistorySeriesAddDate = DateTime.Now, - }; - crunInstance.HistoryList.Add(historySeries); - var newSeason = NewHistorySeason(episode); - - await RefreshSeriesData(seriesId, historySeries); - - historySeries.Seasons.Add(newSeason); - historySeries.UpdateNewEpisodes(); - historySeries.Init(); - newSeason.Init(); - } - - SortItems(); - SortSeasons(historySeries); - } - - public async Task UpdateWithSeasonData(CrunchyEpisodeList seasonData, bool skippVersionCheck = true){ - if (seasonData.Data != null){ + public async Task UpdateWithSeasonData(List? episodeList, bool skippVersionCheck = true){ + if (episodeList != null){ if (!skippVersionCheck){ - if (seasonData.Data.First().Versions != null){ - var version = seasonData.Data.First().Versions.Find(a => a.Original); - if (version.AudioLocale != seasonData.Data.First().AudioLocale){ - CRUpdateSeries(seasonData.Data.First().SeriesId, version.SeasonGuid); + var episodeVersions = episodeList.First().Versions; + if (episodeVersions != null){ + var version = episodeVersions.Find(a => a.Original); + if (version.AudioLocale != episodeList.First().AudioLocale){ + await CRUpdateSeries(episodeList.First().SeriesId, version.SeasonGuid); return; } } else{ - CRUpdateSeries(seasonData.Data.First().SeriesId, ""); + await CRUpdateSeries(episodeList.First().SeriesId, ""); return; } } - var firstEpisode = seasonData.Data.First(); + var firstEpisode = episodeList.First(); var seriesId = firstEpisode.SeriesId; var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); if (historySeries != null){ historySeries.HistorySeriesAddDate ??= DateTime.Now; - var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == firstEpisode.SeasonId); - await RefreshSeriesData(seriesId, historySeries); + var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == firstEpisode.SeasonId); + if (historySeason != null){ historySeason.SeasonTitle = firstEpisode.SeasonTitle; historySeason.SeasonNum = Helpers.ExtractNumberAfterS(firstEpisode.Identifier) ?? firstEpisode.SeasonNumber + ""; historySeason.SpecialSeason = CheckStringForSpecial(firstEpisode.Identifier); - foreach (var crunchyEpisode in seasonData.Data){ + foreach (var crunchyEpisode in episodeList){ var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == crunchyEpisode.Id); if (historyEpisode == null){ var newHistoryEpisode = new HistoryEpisode{ - EpisodeTitle = crunchyEpisode.Identifier.Contains("|M|") ? crunchyEpisode.SeasonTitle : crunchyEpisode.Title, + EpisodeTitle = GetEpisodeTitle(crunchyEpisode), EpisodeDescription = crunchyEpisode.Description, EpisodeId = crunchyEpisode.Id, Episode = crunchyEpisode.Episode, @@ -332,7 +254,7 @@ public class History(){ historySeason.EpisodesList.Add(newHistoryEpisode); } else{ //Update existing episode - historyEpisode.EpisodeTitle = crunchyEpisode.Identifier.Contains("|M|") ? crunchyEpisode.SeasonTitle : crunchyEpisode.Title; + historyEpisode.EpisodeTitle = GetEpisodeTitle(crunchyEpisode); historyEpisode.SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _); historyEpisode.EpisodeDescription = crunchyEpisode.Description; historyEpisode.EpisodeId = crunchyEpisode.Id; @@ -343,7 +265,7 @@ public class History(){ historySeason.EpisodesList.Sort(new NumericStringPropertyComparer()); } else{ - var newSeason = NewHistorySeason(seasonData, firstEpisode); + var newSeason = NewHistorySeason(episodeList, firstEpisode); newSeason.EpisodesList.Sort(new NumericStringPropertyComparer()); @@ -361,7 +283,7 @@ public class History(){ }; crunInstance.HistoryList.Add(historySeries); - var newSeason = NewHistorySeason(seasonData, firstEpisode); + var newSeason = NewHistorySeason(episodeList, firstEpisode); newSeason.EpisodesList.Sort(new NumericStringPropertyComparer()); @@ -381,6 +303,34 @@ public class History(){ private CrSeriesBase? cachedSeries; + private string GetEpisodeTitle(CrunchyEpisode crunchyEpisode){ + if (crunchyEpisode.Identifier.Contains("|M|")){ + if (string.IsNullOrEmpty(crunchyEpisode.Title)){ + if (crunchyEpisode.SeasonTitle.StartsWith(crunchyEpisode.SeriesTitle)){ + var splitTitle = crunchyEpisode.SeasonTitle.Split(new[]{ crunchyEpisode.SeriesTitle }, StringSplitOptions.None); + var titlePart = splitTitle.Length > 1 ? splitTitle[1] : splitTitle[0]; + var cleanedTitle = Regex.Replace(titlePart, @"^[^a-zA-Z]+", ""); + + return cleanedTitle; + } + + return crunchyEpisode.SeasonTitle; + } + + if (crunchyEpisode.Title.StartsWith(crunchyEpisode.SeriesTitle)){ + var splitTitle = crunchyEpisode.Title.Split(new[]{ crunchyEpisode.SeriesTitle }, StringSplitOptions.None); + var titlePart = splitTitle.Length > 1 ? splitTitle[1] : splitTitle[0]; + var cleanedTitle = Regex.Replace(titlePart, @"^[^a-zA-Z]+", ""); + + return cleanedTitle; + } + + return crunchyEpisode.Title; + } + + return crunchyEpisode.Title; + } + private async Task RefreshSeriesData(string seriesId, HistorySeries historySeries){ if (cachedSeries == null || (cachedSeries.Data != null && cachedSeries.Data.First().Id != seriesId)){ cachedSeries = await crunInstance.CrSeries.SeriesById(seriesId, string.IsNullOrEmpty(crunInstance.CrunOptions.HistoryLang) ? crunInstance.DefaultLocale : crunInstance.CrunOptions.HistoryLang, true); @@ -418,7 +368,7 @@ public class History(){ public void SortItems(){ 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; + DateTime today = DateTime.Now.Date; switch (currentSortingType){ case SortingType.SeriesTitle: var sortedList = sortingDir @@ -479,7 +429,7 @@ public class History(){ } } - public static DateTime? ParseDate(string dateStr, DateTime today){ + public DateTime? ParseDate(string dateStr, DateTime today){ if (dateStr == "Today"){ return today; } @@ -502,7 +452,7 @@ public class History(){ return ""; } - private static bool CheckStringForSpecial(string identifier){ + private bool CheckStringForSpecial(string identifier){ if (string.IsNullOrEmpty(identifier)){ return false; } @@ -514,7 +464,7 @@ public class History(){ return Regex.IsMatch(identifier, pattern); } - private static HistorySeason NewHistorySeason(CrunchyEpisodeList seasonData, CrunchyEpisode firstEpisode){ + private HistorySeason NewHistorySeason(List seasonData, CrunchyEpisode firstEpisode){ var newSeason = new HistorySeason{ SeasonTitle = firstEpisode.SeasonTitle, SeasonId = firstEpisode.SeasonId, @@ -523,9 +473,9 @@ public class History(){ SpecialSeason = CheckStringForSpecial(firstEpisode.Identifier) }; - foreach (var crunchyEpisode in seasonData.Data!){ + foreach (var crunchyEpisode in seasonData){ var newHistoryEpisode = new HistoryEpisode{ - EpisodeTitle = crunchyEpisode.Identifier.Contains("|M|") ? crunchyEpisode.SeasonTitle : crunchyEpisode.Title, + EpisodeTitle = GetEpisodeTitle(crunchyEpisode), EpisodeDescription = crunchyEpisode.Description, EpisodeId = crunchyEpisode.Id, Episode = crunchyEpisode.Episode, @@ -538,31 +488,8 @@ public class History(){ return newSeason; } - - private static HistorySeason NewHistorySeason(CrunchyEpisode episode){ - var newSeason = new HistorySeason{ - SeasonTitle = episode.SeasonTitle, - SeasonId = episode.SeasonId, - SeasonNum = Helpers.ExtractNumberAfterS(episode.Identifier) ?? episode.SeasonNumber + "", - EpisodesList =[], - }; - - var newHistoryEpisode = new HistoryEpisode{ - EpisodeTitle = episode.Identifier.Contains("|M|") ? episode.SeasonTitle : episode.Title, - EpisodeDescription = episode.Description, - EpisodeId = episode.Id, - Episode = episode.Episode, - EpisodeSeasonNum = Helpers.ExtractNumberAfterS(episode.Identifier) ?? episode.SeasonNumber + "", - SpecialEpisode = !int.TryParse(episode.Episode, out _), - }; - - newSeason.EpisodesList.Add(newHistoryEpisode); - - - return newSeason; - } - - public async void MatchHistorySeriesWithSonarr(bool updateAll){ + + public void MatchHistorySeriesWithSonarr(bool updateAll){ if (crunInstance.CrunOptions.SonarrProperties is{ SonarrEnabled: false }){ return; } @@ -604,7 +531,7 @@ public class History(){ // Create a copy of the episodes list for each thread var episodesCopy = new List(episodes); - var episode = FindClosestMatchEpisodes(episodesCopy, historyEpisode.EpisodeTitle); + var episode = FindClosestMatchEpisodes(episodesCopy, historyEpisode.EpisodeTitle ?? string.Empty); if (episode != null){ historyEpisode.SonarrEpisodeId = episode.Id + ""; historyEpisode.SonarrEpisodeNumber = episode.EpisodeNumber + ""; diff --git a/CRD/Utils/Files/CfgManager.cs b/CRD/Utils/Files/CfgManager.cs index 7b8a175..ec16219 100644 --- a/CRD/Utils/Files/CfgManager.cs +++ b/CRD/Utils/Files/CfgManager.cs @@ -22,6 +22,7 @@ public class CfgManager{ public static readonly string PathCrToken = WorkingDirectory + "/config/cr_token.yml"; public static readonly string PathCrDownloadOptions = WorkingDirectory + "/config/settings.yml"; public static readonly string PathCrHistory = WorkingDirectory + "/config/history.json"; + public static readonly string PathWindowSettings= WorkingDirectory + "/config/windowSettings.json"; public static readonly string PathFFMPEG = WorkingDirectory + "/lib/ffmpeg.exe"; public static readonly string PathMKVMERGE = WorkingDirectory + "/lib/mkvmerge.exe"; diff --git a/CRD/Utils/Structs/History/HistorySeries.cs b/CRD/Utils/Structs/History/HistorySeries.cs index 502368c..148b1de 100644 --- a/CRD/Utils/Structs/History/HistorySeries.cs +++ b/CRD/Utils/Structs/History/HistorySeries.cs @@ -322,7 +322,7 @@ public class HistorySeries : INotifyPropertyChanged{ await CrunchyrollManager.Instance.History.CRUpdateSeries(SeriesId, seasonId); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesTitle))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesDescription))); - CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, this); + // CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, this); UpdateNewEpisodes(); FetchingData = false; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData))); diff --git a/CRD/Utils/Structs/Structs.cs b/CRD/Utils/Structs/Structs.cs index ed25488..4677062 100644 --- a/CRD/Utils/Structs/Structs.cs +++ b/CRD/Utils/Structs/Structs.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using CRD.Views; using Newtonsoft.Json; namespace CRD.Utils.Structs; @@ -97,4 +99,30 @@ public class FrameData{ public class StringItem{ public string stringValue{ get; set; } +} + +public class WindowSettings{ + public double Width{ get; set; } + public double Height{ get; set; } + public int ScreenIndex{ get; set; } + public int PosX{ get; set; } + public int PosY{ get; set; } +} + +public class ToastMessage(string message, ToastType type, int i){ + public string? Message{ get; set; } = message; + public int Seconds{ get; set; } = i; + public ToastType Type{ get; set; } = type; +} + +public class NavigationMessage{ + public Type? ViewModelType{ get; } + public bool Back{ get; } + public bool Refresh{ get; } + + public NavigationMessage(Type? viewModelType, bool back, bool refresh){ + ViewModelType = viewModelType; + Back = back; + Refresh = refresh; + } } \ No newline at end of file diff --git a/CRD/ViewModels/AccountPageViewModel.cs b/CRD/ViewModels/AccountPageViewModel.cs index bcb25b4..48153cd 100644 --- a/CRD/ViewModels/AccountPageViewModel.cs +++ b/CRD/ViewModels/AccountPageViewModel.cs @@ -30,18 +30,28 @@ public partial class AccountPageViewModel : ViewModelBase{ private static DispatcherTimer? _timer; private DateTime _targetTime; + private bool IsCancelled = false; private bool UnknownEndDate = false; + private bool EndedButMaybeActive = false; public AccountPageViewModel(){ UpdatetProfile(); } private void Timer_Tick(object sender, EventArgs e){ - var remaining = _targetTime - DateTime.UtcNow; + var remaining = _targetTime - DateTime.Now; if (remaining <= TimeSpan.Zero){ RemainingTime = "No active Subscription"; _timer.Stop(); + if (UnknownEndDate){ + RemainingTime = "Unknown Subscription end date"; + } + + if (EndedButMaybeActive){ + RemainingTime = "Subscription maybe ended"; + } + if (CrunchyrollManager.Instance.Profile.Subscription != null){ Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); } @@ -51,25 +61,26 @@ public partial class AccountPageViewModel : ViewModelBase{ } public void UpdatetProfile(){ - ProfileName = CrunchyrollManager.Instance.Profile.Username; // Default or fetched user name + 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 (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 (CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.Count >= 1){ - var sub = CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.First(); - if (sub != null){ - IsCancelled = !sub.AutoRenew; - } - }else if(CrunchyrollManager.Instance.Profile.Subscription?.NonrecurringSubscriptionProducts.Count >= 1){ + var subscriptions = CrunchyrollManager.Instance.Profile.Subscription; + + if (subscriptions != null){ + if (subscriptions.SubscriptionProducts is{ Count: >= 1 }){ + var sub = subscriptions.SubscriptionProducts.First(); + IsCancelled = sub.IsCancelled; + EndedButMaybeActive = !subscriptions.IsActive; + } else if (subscriptions.ThirdPartySubscriptionProducts is{ Count: >= 1 }){ + var sub = subscriptions.ThirdPartySubscriptionProducts.First(); + IsCancelled = !sub.AutoRenew; + EndedButMaybeActive = !subscriptions.IsActive; + } else if (subscriptions.NonrecurringSubscriptionProducts is{ Count: >= 1 }){ IsCancelled = true; - }else if(CrunchyrollManager.Instance.Profile.Subscription?.FunimationSubscriptions.Count >= 1){ + EndedButMaybeActive = !subscriptions.IsActive; + } else if (subscriptions.FunimationSubscriptions is{ Count: >= 1 }){ IsCancelled = true; UnknownEndDate = true; } @@ -82,24 +93,27 @@ public partial class AccountPageViewModel : ViewModelBase{ _timer.Tick += Timer_Tick; _timer.Start(); } - } else{ RemainingTime = "No active Subscription"; if (_timer != null){ _timer.Stop(); _timer.Tick -= Timer_Tick; } + RaisePropertyChanged(nameof(RemainingTime)); if (CrunchyrollManager.Instance.Profile.Subscription != null){ Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); } - } if (UnknownEndDate){ RemainingTime = "Unknown Subscription end date"; } + + if (EndedButMaybeActive){ + RemainingTime = "Subscription maybe ended"; + } } [RelayCommand] diff --git a/CRD/Views/MainWindow.axaml.cs b/CRD/Views/MainWindow.axaml.cs index 7fdafcf..1ef236c 100644 --- a/CRD/Views/MainWindow.axaml.cs +++ b/CRD/Views/MainWindow.axaml.cs @@ -1,13 +1,20 @@ using System; using System.Collections.Generic; +using System.IO; +using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; +using Avalonia.Platform; +using CRD.Utils; +using CRD.Utils.Structs; using CRD.Utils.Updater; using CRD.ViewModels; +using CRD.Views; using CRD.Views.Utils; using FluentAvalonia.Core; using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Windowing; +using Newtonsoft.Json; using ReactiveUI; using ContentDialogUpdateViewModel = CRD.ViewModels.Utils.ContentDialogUpdateViewModel; @@ -44,7 +51,10 @@ public partial class MainWindow : AppWindow{ public MainWindow(){ AvaloniaXamlLoader.Load(this); InitializeComponent(); - + + Opened += OnOpened; + Closing += OnClosing; + TitleBar.ExtendsContentIntoTitleBar = true; TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex; @@ -62,6 +72,7 @@ public partial class MainWindow : AppWindow{ if (viewModel is SeriesPageViewModel){ ((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider); } + navigationStack.Push(viewModel); nv.Content = viewModel; } else if (!message.Back && message.ViewModelType != null){ @@ -69,6 +80,7 @@ public partial class MainWindow : AppWindow{ if (viewModel is SeriesPageViewModel){ ((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider); } + navigationStack.Push(viewModel); nv.Content = viewModel; } else{ @@ -117,9 +129,10 @@ public partial class MainWindow : AppWindow{ break; case "History": navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel)); - if ( navView.Content is HistoryPageViewModel){ + if (navView.Content is HistoryPageViewModel){ ((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider); } + navigationStack.Clear(); navigationStack.Push(navView.Content); selectedNavVieItem = selectedItem; @@ -159,22 +172,55 @@ public partial class MainWindow : AppWindow{ _ = await dialog.ShowAsync(); } -} -public class ToastMessage(string message, ToastType type, int i){ - public string? Message{ get; set; } = message; - public int Seconds{ get; set; } = i; - public ToastType Type{ get; set; } = type; -} + private void OnOpened(object sender, EventArgs e){ + if (File.Exists(CfgManager.PathWindowSettings)){ + var settings = JsonConvert.DeserializeObject(File.ReadAllText(CfgManager.PathWindowSettings)); + if (settings != null){ + Width = settings.Width; + Height = settings.Height; -public class NavigationMessage{ - public Type? ViewModelType{ get; } - public bool Back{ get; } - public bool Refresh{ get; } + var screens = Screens.All; + if (settings.ScreenIndex >= 0 && settings.ScreenIndex < screens.Count){ + var screen = screens[settings.ScreenIndex]; + var screenBounds = screen.Bounds; - public NavigationMessage(Type? viewModelType, bool back, bool refresh){ - ViewModelType = viewModelType; - Back = back; - Refresh = refresh; + var topLeft = screenBounds.TopLeft; + var bottomRight = screenBounds.BottomRight; + + if (settings.PosX >= topLeft.X && settings.PosX <= bottomRight.X - Width && + settings.PosY >= topLeft.Y && settings.PosY <= bottomRight.Y - Height){ + Position = new PixelPoint(settings.PosX, settings.PosY); + } else{ + Position = new PixelPoint(topLeft.X, topLeft.Y + 31); + } + } else{ + var primaryScreen = screens?[0].Bounds ?? new PixelRect(0, 0, 1000, 600); // Default size if no screens + Position = new PixelPoint(primaryScreen.TopLeft.X, primaryScreen.TopLeft.Y + 31); + } + } + } + } + + private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e){ + var screens = Screens.All; + int screenIndex = 0; + + for (int i = 0; i < screens.Count; i++){ + if (screens[i].Bounds.Contains(Position)){ + screenIndex = i; + break; + } + } + + var settings = new WindowSettings{ + Width = Width, + Height = Height, + ScreenIndex = screenIndex, + PosX = Position.X, + PosY = Position.Y + }; + + File.WriteAllText(CfgManager.PathWindowSettings, JsonConvert.SerializeObject(settings, Formatting.Indented)); } } \ No newline at end of file