diff --git a/CRD/Assets/coming_soon_ep.jpg b/CRD/Assets/coming_soon_ep.jpg new file mode 100644 index 0000000..a100f26 Binary files /dev/null and b/CRD/Assets/coming_soon_ep.jpg differ diff --git a/CRD/Downloader/CalendarManager.cs b/CRD/Downloader/CalendarManager.cs index f687318..bee8f6d 100644 --- a/CRD/Downloader/CalendarManager.cs +++ b/CRD/Downloader/CalendarManager.cs @@ -205,7 +205,7 @@ public class CalendarManager{ 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.ThumbnailUrl = crBrowseEpisode.Images.Thumbnail?.FirstOrDefault()?.FirstOrDefault().Source ?? ""; //https://www.crunchyroll.com/i/coming_soon_beta_thumb.jpg calEpisode.IsPremiumOnly = crBrowseEpisode.EpisodeMetadata.IsPremiumOnly; calEpisode.IsPremiere = crBrowseEpisode.EpisodeMetadata.Episode == "1"; calEpisode.SeasonName = crBrowseEpisode.EpisodeMetadata.SeasonTitle; diff --git a/CRD/Downloader/Crunchyroll/CRAuth.cs b/CRD/Downloader/Crunchyroll/CRAuth.cs index 205dd13..4298b70 100644 --- a/CRD/Downloader/Crunchyroll/CRAuth.cs +++ b/CRD/Downloader/Crunchyroll/CRAuth.cs @@ -127,7 +127,6 @@ public class CrAuth{ } else{ crunInstance.Profile.HasPremium = subsc.IsActive; } - } else{ crunInstance.Profile.HasPremium = false; Console.Error.WriteLine("Failed to check premium subscription status"); diff --git a/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs index 69902aa..86724a0 100644 --- a/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs +++ b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs @@ -19,7 +19,6 @@ using CRD.Utils.Muxing; using CRD.Utils.Sonarr; using CRD.Utils.Structs; using CRD.Utils.Structs.History; -using CRD.ViewModels; using CRD.Views; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -34,7 +33,7 @@ public class CrunchyrollManager{ public CrProfile Profile = new(); private readonly Lazy _optionsLazy; public CrDownloadOptions CrunOptions => _optionsLazy.Value; - + #region History Variables public ObservableCollection HistoryList = new(); @@ -42,7 +41,7 @@ public class CrunchyrollManager{ public HistorySeries SelectedSeries = new HistorySeries{ Seasons =[] }; - + #endregion @@ -58,7 +57,7 @@ public class CrunchyrollManager{ public CrEpisode CrEpisode; public CrSeries CrSeries; public History History; - + #region Singelton private static CrunchyrollManager? _instance; @@ -86,9 +85,8 @@ public class CrunchyrollManager{ private CrDownloadOptions InitDownloadOptions(){ - var options = new CrDownloadOptions(); - + options.AutoDownload = false; options.RemoveFinishedDownload = false; options.Chapters = true; @@ -123,7 +121,7 @@ public class CrunchyrollManager{ return options; } - + public void InitOptions(){ _widevine = Widevine.Instance; @@ -144,8 +142,6 @@ public class CrunchyrollManager{ } public async Task Init(){ - - if (CrunOptions.LogMode){ CfgManager.EnableLogMode(); } else{ @@ -181,7 +177,6 @@ public class CrunchyrollManager{ } - public async Task DownloadEpisode(CrunchyEpMeta data, CrDownloadOptions options){ QueueManager.Instance.ActiveDownloads++; @@ -369,7 +364,7 @@ public class CrunchyrollManager{ } } } - + syncVideosList.ForEach(syncVideo => Helpers.DeleteFile(syncVideo.Path)); } @@ -648,14 +643,13 @@ public class CrunchyrollManager{ } dlFailed = true; - + return new DownloadResponse{ Data = new List(), Error = dlFailed, FileName = "./unknown", ErrorText = "Hardsubs not available" }; - } } else{ streams = streams.Where((s) => { @@ -1601,27 +1595,42 @@ public class CrunchyrollManager{ var showRequestResponse = await HttpClientReq.Instance.SendHttpRequest(showRequest); if (showRequestResponse.IsOk){ - JObject jObject = JObject.Parse(showRequestResponse.ResponseContent); - CrunchyChapters chapterData = new CrunchyChapters(); - chapterData.lastUpdate = jObject["lastUpdate"]?.ToObject(); - chapterData.mediaId = jObject["mediaId"]?.ToObject(); chapterData.Chapters = new List(); - foreach (var property in jObject.Properties()){ - // Check if the property value is an object and the property is not one of the known non-dictionary properties - if (property.Value.Type == JTokenType.Object && property.Name != "lastUpdate" && property.Name != "mediaId"){ - // Deserialize the property value into a CrunchyChapter and add it to the dictionary - CrunchyChapter chapter = property.Value.ToObject(); - chapterData.Chapters.Add(chapter); + try{ + JObject jObject = JObject.Parse(showRequestResponse.ResponseContent); + + if (jObject.TryGetValue("lastUpdate", out JToken lastUpdateToken)){ + chapterData.lastUpdate = lastUpdateToken.ToObject(); } + + if (jObject.TryGetValue("mediaId", out JToken mediaIdToken)){ + chapterData.mediaId = mediaIdToken.ToObject(); + } + + chapterData.Chapters = new List(); + + foreach (var property in jObject.Properties()){ + if (property.Value.Type == JTokenType.Object && property.Name != "lastUpdate" && property.Name != "mediaId"){ + try{ + CrunchyChapter chapter = property.Value.ToObject(); + chapterData.Chapters.Add(chapter); + } catch (Exception ex){ + Console.Error.WriteLine($"Error parsing chapter: {ex.Message}"); + } + } + } + } catch (Exception ex){ + Console.Error.WriteLine($"Error parsing JSON response: {ex.Message}"); } if (chapterData.Chapters.Count > 0){ chapterData.Chapters.Sort((a, b) => { - if (a.start != null && b.start != null) - return a.start.Value - b.start.Value; - return 0; + if (a.start.HasValue && b.start.HasValue){ + return a.start.Value.CompareTo(b.start.Value); + } + return 0; // Both values are null, they are considered equal }); if (!((chapterData.Chapters.Any(c => c.type == "intro")) || chapterData.Chapters.Any(c => c.type == "recap"))){ diff --git a/CRD/Utils/Muxing/Merger.cs b/CRD/Utils/Muxing/Merger.cs index 7dcb1c9..c05170c 100644 --- a/CRD/Utils/Muxing/Merger.cs +++ b/CRD/Utils/Muxing/Merger.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Xml; +using CRD.Downloader.Crunchyroll; using CRD.Utils.Structs; using DynamicData; @@ -191,7 +192,7 @@ public class Merger{ args.Add($"--track-name {trackName}"); args.Add($"--language 0:\"{subObj.Language.Code}\""); - if (options.Defaults.Sub.Code == subObj.Language.Code && subObj.ClosedCaption == false){ + if (options.Defaults.Sub.Code == subObj.Language.Code && CrunchyrollManager.Instance.CrunOptions.DefaultSubSigns == subObj.Signs && subObj.ClosedCaption == false){ args.Add("--default-track 0"); } else{ args.Add("--default-track 0:0"); diff --git a/CRD/Utils/Structs/CalendarStructs.cs b/CRD/Utils/Structs/CalendarStructs.cs index e2cfc8f..a79b848 100644 --- a/CRD/Utils/Structs/CalendarStructs.cs +++ b/CRD/Utils/Structs/CalendarStructs.cs @@ -2,9 +2,12 @@ using System.Collections.Generic; using System.ComponentModel; using System.Net.Http; +using System.Reflection; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Avalonia; using Avalonia.Media.Imaging; +using Avalonia.Platform; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; using CRD.Downloader.Crunchyroll; @@ -33,7 +36,7 @@ public partial class CalendarEpisode : INotifyPropertyChanged{ public Bitmap? ImageBitmap{ get; set; } public string? EpisodeNumber{ get; set; } - + public bool IsPremiumOnly{ get; set; } public bool IsPremiere{ get; set; } @@ -48,18 +51,22 @@ 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 - QueueManager.Instance.CRAddEpisodeToQue(id, Languages.Locale2language(locale).CrLocale, CrunchyrollManager.Instance.CrunOptions.DubLang,true); + QueueManager.Instance.CRAddEpisodeToQue(id, Languages.Locale2language(locale).CrLocale, CrunchyrollManager.Instance.CrunOptions.DubLang, true); } } - + public async Task LoadImage(){ try{ - using (var client = new HttpClient()){ - var response = await client.GetAsync(ThumbnailUrl); - response.EnsureSuccessStatusCode(); - using (var stream = await response.Content.ReadAsStreamAsync()){ - ImageBitmap = new Bitmap(stream); - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageBitmap))); + if (string.IsNullOrEmpty(ThumbnailUrl)){ + + } else{ + using (var client = new HttpClient()){ + var response = await client.GetAsync(ThumbnailUrl); + response.EnsureSuccessStatusCode(); + using (var stream = await response.Content.ReadAsStreamAsync()){ + ImageBitmap = new Bitmap(stream); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImageBitmap))); + } } } } catch (Exception ex){ diff --git a/CRD/Utils/Structs/Chapters.cs b/CRD/Utils/Structs/Chapters.cs index 2804922..197375d 100644 --- a/CRD/Utils/Structs/Chapters.cs +++ b/CRD/Utils/Structs/Chapters.cs @@ -13,12 +13,12 @@ public struct CrunchyChapters{ public struct CrunchyChapter{ public string approverId { get; set; } public string distributionNumber { get; set; } - public int? end { get; set; } - public int? start { get; set; } + public double? end { get; set; } + public double? start { get; set; } public string title { get; set; } public string seriesId { get; set; } [JsonProperty("new")] - public bool New { get; set; } + public string? New { get; set; } public string type { get; set; } } diff --git a/CRD/Utils/Structs/CrDownloadOptions.cs b/CRD/Utils/Structs/CrDownloadOptions.cs index 3640dd8..c8df7db 100644 --- a/CRD/Utils/Structs/CrDownloadOptions.cs +++ b/CRD/Utils/Structs/CrDownloadOptions.cs @@ -95,6 +95,9 @@ public class CrDownloadOptions{ [YamlMember(Alias = "mux_default_sub", ApplyNamingConventions = false)] public string DefaultSub{ get; set; } + + [YamlMember(Alias = "mux_default_sub_signs", ApplyNamingConventions = false)] + public bool DefaultSubSigns{ get; set; } [YamlMember(Alias = "mux_default_dub", ApplyNamingConventions = false)] public string DefaultAudio{ get; set; } diff --git a/CRD/Utils/Structs/EpisodeStructs.cs b/CRD/Utils/Structs/EpisodeStructs.cs index 44850ac..9dce293 100644 --- a/CRD/Utils/Structs/EpisodeStructs.cs +++ b/CRD/Utils/Structs/EpisodeStructs.cs @@ -185,7 +185,7 @@ public struct Images{ [JsonProperty("promo_image")] public List>? PromoImage{ get; set; } - public List> Thumbnail{ get; set; } + public List>? Thumbnail{ get; set; } } public struct Image{ diff --git a/CRD/Utils/Structs/Languages.cs b/CRD/Utils/Structs/Languages.cs index 6c26f7e..6b77673 100644 --- a/CRD/Utils/Structs/Languages.cs +++ b/CRD/Utils/Structs/Languages.cs @@ -38,6 +38,7 @@ public class Languages{ new(){ CrLocale = "te-IN", Locale = "te-IN", Code = "tel", Name = "Telugu (India)", Language = "తెలుగు" }, new(){ CrLocale = "id-ID", Locale = "id", Code = "in", Name = "Indonesian " } }; + public static LanguageItem FixAndFindCrLc(string cr_locale){ if (string.IsNullOrEmpty(cr_locale)){ diff --git a/CRD/ViewModels/AccountPageViewModel.cs b/CRD/ViewModels/AccountPageViewModel.cs index 496650b..8099f78 100644 --- a/CRD/ViewModels/AccountPageViewModel.cs +++ b/CRD/ViewModels/AccountPageViewModel.cs @@ -11,6 +11,7 @@ using CRD.Downloader.Crunchyroll; using CRD.Utils.Structs; using CRD.Views.Utils; using FluentAvalonia.UI.Controls; +using Newtonsoft.Json; namespace CRD.ViewModels; @@ -40,6 +41,9 @@ public partial class AccountPageViewModel : ViewModelBase{ if (remaining <= TimeSpan.Zero){ RemainingTime = "No active Subscription"; _timer.Stop(); + if (CrunchyrollManager.Instance.Profile.Subscription != null){ + Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); + } } else{ RemainingTime = $"{(IsCancelled ? "Subscription ending in: " : "Subscription refreshing in: ")}{remaining:dd\\:hh\\:mm\\:ss}"; } @@ -82,6 +86,11 @@ public partial class AccountPageViewModel : ViewModelBase{ _timer.Tick -= Timer_Tick; } RaisePropertyChanged(nameof(RemainingTime)); + + if (CrunchyrollManager.Instance.Profile.Subscription != null){ + Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); + } + } } diff --git a/CRD/ViewModels/CalendarPageViewModel.cs b/CRD/ViewModels/CalendarPageViewModel.cs index e8ac7fb..13cfd84 100644 --- a/CRD/ViewModels/CalendarPageViewModel.cs +++ b/CRD/ViewModels/CalendarPageViewModel.cs @@ -1,8 +1,14 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using Avalonia; using Avalonia.Controls; +using Avalonia.Media.Imaging; +using Avalonia.Platform; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Downloader; @@ -56,8 +62,9 @@ public partial class CalendarPageViewModel : ViewModelBase{ private CalendarWeek? currentWeek; private bool loading = true; - + public CalendarPageViewModel(){ + CalendarDays = new ObservableCollection(); foreach (var languageItem in Languages.languages){ @@ -75,7 +82,7 @@ public partial class CalendarPageViewModel : ViewModelBase{ loading = false; LoadCalendar(GetThisWeeksMondayDate(), false); } - + private string GetThisWeeksMondayDate(){ // Get today's date DateTime today = DateTime.Today; @@ -226,7 +233,7 @@ public partial class CalendarPageViewModel : ViewModelBase{ CrunchyrollManager.Instance.CrunOptions.CustomCalendar = value; LoadCalendar(GetThisWeeksMondayDate(), true); - + CfgManager.WriteSettingsToFile(); } diff --git a/CRD/ViewModels/SettingsPageViewModel.cs b/CRD/ViewModels/SettingsPageViewModel.cs index 867d5d2..0ff7d0d 100644 --- a/CRD/ViewModels/SettingsPageViewModel.cs +++ b/CRD/ViewModels/SettingsPageViewModel.cs @@ -57,6 +57,8 @@ public partial class SettingsPageViewModel : ViewModelBase{ [ObservableProperty] private bool _syncTimings; + [ObservableProperty] + private bool _defaultSubSigns; [ObservableProperty] private bool _includeEpisodeDescription; @@ -380,6 +382,7 @@ public partial class SettingsPageViewModel : ViewModelBase{ AddScaledBorderAndShadow = options.SubsAddScaledBorder is ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo or ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes; SelectedScaledBorderAndShadow = GetScaledBorderAndShadowFromOptions(options); + DefaultSubSigns = options.DefaultSubSigns; HistoryAddSpecials = options.HistoryAddSpecials; DownloadSpeed = options.DownloadSpeedLimit; IncludeEpisodeDescription = options.IncludeVideoDescription; @@ -445,7 +448,8 @@ public partial class SettingsPageViewModel : ViewModelBase{ if (!settingsLoaded){ return; } - + + CrunchyrollManager.Instance.CrunOptions.DefaultSubSigns = DefaultSubSigns; CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription; CrunchyrollManager.Instance.CrunOptions.HistoryAddSpecials = HistoryAddSpecials; CrunchyrollManager.Instance.CrunOptions.VideoTitle = FileTitle; diff --git a/CRD/Views/CalendarPageView.axaml b/CRD/Views/CalendarPageView.axaml index e93d104..1eca84c 100644 --- a/CRD/Views/CalendarPageView.axaml +++ b/CRD/Views/CalendarPageView.axaml @@ -177,7 +177,12 @@ Text="{Binding DateTime, StringFormat='hh:mm tt'}" Margin="0,0,0,0" /> - + + + + + + + + + + + +