diff --git a/CRD/Downloader/CrSeries.cs b/CRD/Downloader/CrSeries.cs index faa7352..2aa2036 100644 --- a/CRD/Downloader/CrSeries.cs +++ b/CRD/Downloader/CrSeries.cs @@ -137,7 +137,7 @@ public class CrSeries(Crunchyroll crunInstance){ var s = result[season][key]; if (data?.S != null && s.Id != data.Value.S) continue; int fallbackIndex = 0; - var seasonData = await GetSeasonDataById(s); + var seasonData = await GetSeasonDataById(s.Id); if (seasonData.Data != null){ if (crunInstance.CrunOptions.History){ @@ -268,7 +268,7 @@ public class CrSeries(Crunchyroll crunInstance){ return crunchySeriesList; } - public async Task GetSeasonDataById(SeriesSearchItem item, bool log = false){ + public async Task GetSeasonDataById(string seasonID, bool log = false){ CrunchyEpisodeList episodeList = new CrunchyEpisodeList(){ Data = new List(), Total = 0, Meta = new Meta() }; if (crunInstance.CmsToken?.Cms == null){ @@ -277,7 +277,7 @@ public class CrSeries(Crunchyroll crunInstance){ } if (log){ - var showRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/seasons/{item.Id}?preferred_audio_language=ja-JP", HttpMethod.Get, true, true, null); + var showRequest = HttpClientReq.CreateRequestMessage($"{Api.Cms}/seasons/{seasonID}?preferred_audio_language=ja-JP", HttpMethod.Get, true, true, null); var response = await HttpClientReq.Instance.SendHttpRequest(showRequest); @@ -290,7 +290,7 @@ public class CrSeries(Crunchyroll crunInstance){ //TODO - var episodeRequest = new HttpRequestMessage(HttpMethod.Get, $"{Api.Cms}/seasons/{item.Id}/episodes?preferred_audio_language=ja-JP"); + var episodeRequest = new HttpRequestMessage(HttpMethod.Get, $"{Api.Cms}/seasons/{seasonID}/episodes?preferred_audio_language=ja-JP"); episodeRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", crunInstance.Token?.access_token); diff --git a/CRD/Downloader/History.cs b/CRD/Downloader/History.cs index 1a58903..96eec47 100644 --- a/CRD/Downloader/History.cs +++ b/CRD/Downloader/History.cs @@ -20,7 +20,7 @@ namespace CRD.Downloader; public class History(Crunchyroll crunInstance){ public async Task UpdateSeries(string seriesId, string? seasonId){ await crunInstance.CrAuth.RefreshToken(true); - + CrSeriesSearch? parsedSeries = await crunInstance.CrSeries.ParseSeriesById(seriesId, "ja"); if (parsedSeries == null){ @@ -35,7 +35,20 @@ public class History(Crunchyroll crunInstance){ foreach (var key in result[season].Keys){ var s = result[season][key]; if (!string.IsNullOrEmpty(seasonId) && s.Id != seasonId) continue; - var seasonData = await crunInstance.CrSeries.GetSeasonDataById(s); + + 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; + } + } + } + + var seasonData = await crunInstance.CrSeries.GetSeasonDataById(sId); UpdateWithSeasonData(seasonData); } } @@ -107,7 +120,7 @@ public class History(Crunchyroll crunInstance){ historySeries.Seasons.Add(newSeason); - historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum).ToList(); + historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList(); } historySeries.UpdateNewEpisodes(); } else{ @@ -148,15 +161,24 @@ public class History(Crunchyroll crunInstance){ if (historySeason != null){ foreach (var crunchyEpisode in seasonData.Data){ - if (historySeason.EpisodesList.All(e => e.EpisodeId != crunchyEpisode.Id)){ + + var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == crunchyEpisode.Id); + + if (historyEpisode == null){ var newHistoryEpisode = new HistoryEpisode{ EpisodeTitle = crunchyEpisode.Title, EpisodeId = crunchyEpisode.Id, Episode = crunchyEpisode.Episode, + SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _), }; historySeason.EpisodesList.Add(newHistoryEpisode); + } else{ + //Update existing episode + historyEpisode.EpisodeTitle = crunchyEpisode.Title; + historyEpisode.SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _); } + } historySeason.EpisodesList.Sort(new NumericStringPropertyComparer()); @@ -167,7 +189,7 @@ public class History(Crunchyroll crunInstance){ historySeries.Seasons.Add(newSeason); - historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum).ToList(); + historySeries.Seasons = historySeries.Seasons.OrderBy(s => s.SeasonNum != null ? int.Parse(s.SeasonNum) : 0).ToList(); } historySeries.UpdateNewEpisodes(); } else{ @@ -190,6 +212,7 @@ public class History(Crunchyroll crunInstance){ newHistorySeries.Seasons.Add(newSeason); + newHistorySeries.UpdateNewEpisodes(); } } @@ -235,6 +258,7 @@ public class History(Crunchyroll crunInstance){ EpisodeTitle = crunchyEpisode.Title, EpisodeId = crunchyEpisode.Id, Episode = crunchyEpisode.Episode, + SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _), }; newSeason.EpisodesList.Add(newHistoryEpisode); @@ -255,6 +279,7 @@ public class History(Crunchyroll crunInstance){ EpisodeTitle = episode.Title, EpisodeId = episode.Id, Episode = episode.Episode, + SpecialEpisode = !int.TryParse(episode.Episode, out _), }; newSeason.EpisodesList.Add(newHistoryEpisode); @@ -269,7 +294,7 @@ public class NumericStringPropertyComparer : IComparer{ if (int.TryParse(x.Episode, out int xInt) && int.TryParse(y.Episode, out int yInt)){ return xInt.CompareTo(yInt); } - + // Fall back to string comparison if not parseable as integers return String.Compare(x.Episode, y.Episode, StringComparison.Ordinal); } @@ -328,7 +353,7 @@ public class HistorySeries : INotifyPropertyChanged{ // Iterate over the Episodes from the end to the beginning for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){ - if (!Seasons[i].EpisodesList[j].WasDownloaded){ + if (!Seasons[i].EpisodesList[j].WasDownloaded && !Seasons[i].EpisodesList[j].SpecialEpisode){ count++; } else{ foundWatched = true; @@ -351,7 +376,7 @@ public class HistorySeries : INotifyPropertyChanged{ // Iterate over the Episodes from the end to the beginning for (int j = Seasons[i].EpisodesList.Count - 1; j >= 0 && !foundWatched; j--){ - if (!Seasons[i].EpisodesList[j].WasDownloaded){ + if (!Seasons[i].EpisodesList[j].WasDownloaded && !Seasons[i].EpisodesList[j].SpecialEpisode){ //ADD to download queue await Seasons[i].EpisodesList[j].DownloadEpisode(); } else{ @@ -419,8 +444,11 @@ public partial class HistoryEpisode : INotifyPropertyChanged{ [JsonProperty("episode_was_downloaded")] public bool WasDownloaded{ get; set; } - public event PropertyChangedEventHandler? PropertyChanged; + [JsonProperty("episode_special_episode")] + public bool SpecialEpisode{ get; set; } + public event PropertyChangedEventHandler? PropertyChanged; + public void ToggleWasDownloaded(){ WasDownloaded = !WasDownloaded; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(WasDownloaded))); diff --git a/CRD/Utils/Structs/CrSeriesSearch.cs b/CRD/Utils/Structs/CrSeriesSearch.cs index 131166a..60f455a 100644 --- a/CRD/Utils/Structs/CrSeriesSearch.cs +++ b/CRD/Utils/Structs/CrSeriesSearch.cs @@ -32,7 +32,7 @@ public struct SeriesSearchItem{ [JsonProperty("season_number")] public int SeasonNumber{ get; set; } public Dictionary Images{ get; set; } [JsonProperty("mature_blocked")] public bool MatureBlocked{ get; set; } - public List Versions{ get; set; } + public List? Versions{ get; set; } public string Title{ get; set; } [JsonProperty("is_subbed")] public bool IsSubbed{ get; set; } public string Id{ get; set; } diff --git a/CRD/ViewModels/HistoryPageViewModel.cs b/CRD/ViewModels/HistoryPageViewModel.cs index 27905b8..b79a9f7 100644 --- a/CRD/ViewModels/HistoryPageViewModel.cs +++ b/CRD/ViewModels/HistoryPageViewModel.cs @@ -4,59 +4,77 @@ using System.Linq; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Utils; using CRD.Views; using ReactiveUI; namespace CRD.ViewModels; public partial class HistoryPageViewModel : ViewModelBase{ - public ObservableCollection Items{ get; } - [ObservableProperty] private bool? _showLoading = false; + + [ObservableProperty] + private bool? _showLoading = false; + [ObservableProperty] public HistorySeries _selectedSeries; + [ObservableProperty] + public static bool _editMode; + public HistoryPageViewModel(){ Items = Crunchyroll.Instance.HistoryList; - + foreach (var historySeries in Items){ if (historySeries.ThumbnailImage == null){ historySeries.LoadImage(); } + historySeries.UpdateNewEpisodes(); } - } partial void OnSelectedSeriesChanged(HistorySeries value){ Crunchyroll.Instance.SelectedSeries = value; - MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,false)); + MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, false)); _selectedSeries = null; } - - [RelayCommand] - public void NavToSeries(){ - MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,false)); - } + [RelayCommand] + public void RemoveSeries(string? seriesId){ + + HistorySeries? objectToRemove = Crunchyroll.Instance.HistoryList.ToList().Find(se => se.SeriesId == seriesId) ?? null; + if (objectToRemove != null) { + Crunchyroll.Instance.HistoryList.Remove(objectToRemove); + Items.Remove(objectToRemove); + } + CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList); + } + + + [RelayCommand] + public void NavToSeries(){ + MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel), false, false)); + } + [RelayCommand] public async void RefreshAll(){ - for (int i = 0; i < Items.Count; i++) { + for (int i = 0; i < Items.Count; i++){ ShowLoading = true; await Items[i].FetchData(""); Items[i].UpdateNewEpisodes(); } + ShowLoading = false; } - + [RelayCommand] public async void AddMissingToQueue(){ - for (int i = 0; i < Items.Count; i++) { + for (int i = 0; i < Items.Count; i++){ await Items[i].AddNewMissingToDownloads(); } + ShowLoading = false; } - - } \ No newline at end of file diff --git a/CRD/ViewModels/MainWindowViewModel.cs b/CRD/ViewModels/MainWindowViewModel.cs index e00a45b..d3ba259 100644 --- a/CRD/ViewModels/MainWindowViewModel.cs +++ b/CRD/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Net.Http; using System.Reflection; using System.Threading.Tasks; @@ -23,9 +24,25 @@ public partial class MainWindowViewModel : ViewModelBase{ _faTheme = App.Current.Styles[0] as FluentAvaloniaTheme; Init(); + + CleanUpOldUpdater(); + } + private void CleanUpOldUpdater() { + string backupFilePath = Path.Combine(Directory.GetCurrentDirectory(), "Updater.exe.bak"); + if (File.Exists(backupFilePath)) { + try { + File.Delete(backupFilePath); + Console.WriteLine($"Deleted old updater file: {backupFilePath}"); + } catch (Exception ex) { + Console.WriteLine($"Failed to delete old updater file: {ex.Message}"); + } + } else { + Console.WriteLine("No old updater file found to delete."); + } + } public async void Init(){ diff --git a/CRD/ViewModels/SeriesPageViewModel.cs b/CRD/ViewModels/SeriesPageViewModel.cs index 20b8c9b..561214b 100644 --- a/CRD/ViewModels/SeriesPageViewModel.cs +++ b/CRD/ViewModels/SeriesPageViewModel.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; +using CRD.Utils; using CRD.Views; using ReactiveUI; @@ -14,6 +15,9 @@ public partial class SeriesPageViewModel : ViewModelBase{ [ObservableProperty] public HistorySeries _selectedSeries; + [ObservableProperty] + public static bool _editMode; + public SeriesPageViewModel(){ _selectedSeries = Crunchyroll.Instance.SelectedSeries; @@ -29,6 +33,19 @@ public partial class SeriesPageViewModel : ViewModelBase{ MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,true)); } + [RelayCommand] + public void RemoveSeason(string? season){ + + HistorySeason? objectToRemove = SelectedSeries.Seasons.Find(se => se.SeasonId == season) ?? null; + if (objectToRemove != null) { + SelectedSeries.Seasons.Remove(objectToRemove); + } + CfgManager.WriteJsonToFile(CfgManager.PathCrHistory, Crunchyroll.Instance.HistoryList); + MessageBus.Current.SendMessage(new NavigationMessage(typeof(SeriesPageViewModel),false,true)); + } + + + [RelayCommand] public void NavBack(){ SelectedSeries.UpdateNewEpisodes(); diff --git a/CRD/Views/HistoryPageView.axaml b/CRD/Views/HistoryPageView.axaml index 378673c..9fa626f 100644 --- a/CRD/Views/HistoryPageView.axaml +++ b/CRD/Views/HistoryPageView.axaml @@ -5,6 +5,7 @@ xmlns:vm="clr-namespace:CRD.ViewModels" xmlns:ui="clr-namespace:CRD.Utils.UI" + xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" x:DataType="vm:HistoryPageViewModel" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="CRD.Views.HistoryPageView"> @@ -23,6 +24,7 @@ + Edit @@ -44,20 +46,43 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + diff --git a/CRD/Views/SeriesPageView.axaml b/CRD/Views/SeriesPageView.axaml index 0a1fc7b..9da0a8d 100644 --- a/CRD/Views/SeriesPageView.axaml +++ b/CRD/Views/SeriesPageView.axaml @@ -9,7 +9,7 @@ x:DataType="vm:SeriesPageViewModel" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="CRD.Views.SeriesPageView"> - + @@ -28,26 +28,24 @@ Height="360"> - - - - - - - - - - - - - - - - + - - + + + + + + + + + + + + Edit + + + @@ -58,11 +56,10 @@ - - + @@ -83,20 +80,28 @@ - - - + +