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
This commit is contained in:
Elwador 2024-08-16 14:21:23 +02:00
parent 6aa10cb2c2
commit 9c6ad2d7e8
9 changed files with 229 additions and 224 deletions

View File

@ -44,7 +44,6 @@ public class CrAuth{
PreferredContentAudioLanguage = "ja-JP", PreferredContentAudioLanguage = "ja-JP",
PreferredContentSubtitleLanguage = "de-DE" PreferredContentSubtitleLanguage = "de-DE"
}; };
} }
private void JsonTokenToFileAndVariable(string content){ private void JsonTokenToFileAndVariable(string content){
@ -52,7 +51,7 @@ public class CrAuth{
if (crunInstance.Token != null && crunInstance.Token.expires_in != null){ 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); CfgManager.WriteTokenToYamlFile(crunInstance.Token, CfgManager.PathCrToken);
} }
@ -82,7 +81,8 @@ public class CrAuth{
if (response.ResponseContent.Contains("invalid_credentials")){ if (response.ResponseContent.Contains("invalid_credentials")){
MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - because of invalid login credentials", ToastType.Error, 10)); MessageBus.Current.SendMessage(new ToastMessage($"Failed to login - because of invalid login credentials", ToastType.Error, 10));
} else{ } 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 }){ if (subsc is{ SubscriptionProducts:{ Count: 0 }, ThirdPartySubscriptionProducts.Count: > 0 }){
var thirdPartySub = subsc.ThirdPartySubscriptionProducts.First(); var thirdPartySub = subsc.ThirdPartySubscriptionProducts.First();
var expiration = thirdPartySub.InGrace ? thirdPartySub.InGraceExpirationDate : thirdPartySub.ExpirationDate; var expiration = thirdPartySub.InGrace ? thirdPartySub.InGraceExpirationDate : thirdPartySub.ExpirationDate;
var remaining = expiration - DateTime.UtcNow; var remaining = expiration - DateTime.Now;
crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; crunInstance.Profile.HasPremium = true;
crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; if (crunInstance.Profile.Subscription != null){
crunInstance.Profile.Subscription.NextRenewalDate = expiration; crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero;
crunInstance.Profile.Subscription.NextRenewalDate = expiration;
}
} else if (subsc is{ SubscriptionProducts:{ Count: 0 }, NonrecurringSubscriptionProducts.Count: > 0 }){ } else if (subsc is{ SubscriptionProducts:{ Count: 0 }, NonrecurringSubscriptionProducts.Count: > 0 }){
var nonRecurringSub = subsc.NonrecurringSubscriptionProducts.First(); var nonRecurringSub = subsc.NonrecurringSubscriptionProducts.First();
var remaining = nonRecurringSub.EndDate - DateTime.UtcNow; var remaining = nonRecurringSub.EndDate - DateTime.Now;
crunInstance.Profile.HasPremium = remaining > TimeSpan.Zero; crunInstance.Profile.HasPremium = true;
crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero; if (crunInstance.Profile.Subscription != null){
crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate; crunInstance.Profile.Subscription.IsActive = remaining > TimeSpan.Zero;
crunInstance.Profile.Subscription.NextRenewalDate = nonRecurringSub.EndDate;
}
} else if (subsc is{ SubscriptionProducts:{ Count: 0 }, FunimationSubscriptions.Count: > 0 }){ } else if (subsc is{ SubscriptionProducts:{ Count: 0 }, FunimationSubscriptions.Count: > 0 }){
crunInstance.Profile.HasPremium = true; crunInstance.Profile.HasPremium = true;
} else if (subsc is{ SubscriptionProducts.Count: > 0 }){
crunInstance.Profile.HasPremium = true;
} else{ } else{
crunInstance.Profile.HasPremium = subsc.IsActive; crunInstance.Profile.HasPremium = false;
Console.Error.WriteLine($"No subscription available:\n {JsonConvert.SerializeObject(subsc, Formatting.Indented)} ");
} }
} else{ } else{
crunInstance.Profile.HasPremium = false; crunInstance.Profile.HasPremium = false;
@ -175,7 +182,6 @@ public class CrAuth{
await GetProfile(); await GetProfile();
} }
} }
public async Task RefreshToken(bool needsToken){ public async Task RefreshToken(bool needsToken){
@ -213,7 +219,5 @@ public class CrAuth{
} else{ } else{
Console.Error.WriteLine("Refresh Token Auth Failed"); Console.Error.WriteLine("Refresh Token Auth Failed");
} }
} }
} }

View File

@ -59,7 +59,7 @@ public class CrEpisode(){
CrunchyRollEpisodeData episode = new CrunchyRollEpisodeData(); CrunchyRollEpisodeData episode = new CrunchyRollEpisodeData();
if (crunInstance.CrunOptions.History && updateHistory){ if (crunInstance.CrunOptions.History && updateHistory){
await crunInstance.History.UpdateWithEpisode(dlEpisode); await crunInstance.History.UpdateWithSeasonData(new List<CrunchyEpisode>(){dlEpisode});
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == dlEpisode.SeriesId); var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == dlEpisode.SeriesId);
if (historySeries != null){ if (historySeries != null){
CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(false); CrunchyrollManager.Instance.History.MatchHistorySeriesWithSonarr(false);

View File

@ -16,26 +16,6 @@ namespace CRD.Downloader.Crunchyroll;
public class CrSeries(){ public class CrSeries(){
private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance; private readonly CrunchyrollManager crunInstance = CrunchyrollManager.Instance;
public async Task<List<CrunchyEpMeta>> 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<CrunchyEpMeta>();
}
public Dictionary<string, CrunchyEpMeta> ItemSelectMultiDub(Dictionary<string, EpisodeAndLanguage> eps, List<string> dubLang, bool? but, bool? all, List<string>? e){ public Dictionary<string, CrunchyEpMeta> ItemSelectMultiDub(Dictionary<string, EpisodeAndLanguage> eps, List<string> dubLang, bool? but, bool? all, List<string>? e){
var ret = new Dictionary<string, CrunchyEpMeta>(); var ret = new Dictionary<string, CrunchyEpMeta>();
@ -143,25 +123,31 @@ public class CrSeries(){
CrSeriesSearch? parsedSeries = await ParseSeriesById(id, crLocale); // one piece - GRMG8ZQZR CrSeriesSearch? parsedSeries = await ParseSeriesById(id, crLocale); // one piece - GRMG8ZQZR
if (parsedSeries == null){ if (parsedSeries == null || parsedSeries.Data == null){
Console.Error.WriteLine("Parse Data Invalid"); Console.Error.WriteLine("Parse Data Invalid");
return null; return null;
} }
var result = ParseSeriesResult(parsedSeries); // var result = ParseSeriesResult(parsedSeries);
Dictionary<string, EpisodeAndLanguage> episodes = new Dictionary<string, EpisodeAndLanguage>(); Dictionary<string, EpisodeAndLanguage> episodes = new Dictionary<string, EpisodeAndLanguage>();
if (crunInstance.CrunOptions.History){
crunInstance.History.CRUpdateSeries(id,"");
}
foreach (int season in result.Keys){ var cachedSeasonID = "";
foreach (var key in result[season].Keys){ var seasonData = new CrunchyEpisodeList();
var s = result[season][key];
foreach (var s in parsedSeries.Data){
if (data?.S != null && s.Id != data.Value.S) continue; if (data?.S != null && s.Id != data.Value.S) continue;
int fallbackIndex = 0; 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 (seasonData.Data != null){
if (crunInstance.CrunOptions.History){
crunInstance.History.UpdateWithSeasonData(seasonData, false);
}
foreach (var episode in seasonData.Data){ foreach (var episode in seasonData.Data){
// Prepare the episode array // Prepare the episode array
@ -204,7 +190,6 @@ public class CrSeries(){
} }
} }
} }
}
} }
if (crunInstance.CrunOptions.History){ if (crunInstance.CrunOptions.History){

View File

@ -22,45 +22,40 @@ public class History(){
public async Task CRUpdateSeries(string seriesId, string? seasonId){ public async Task CRUpdateSeries(string seriesId, string? seasonId){
await crunInstance.CrAuth.RefreshToken(true); 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){ if (parsedSeries == null){
Console.Error.WriteLine("Parse Data Invalid"); Console.Error.WriteLine("Parse Data Invalid");
return; return;
} }
var result = crunInstance.CrSeries.ParseSeriesResult(parsedSeries); if (parsedSeries.Data != null){
Dictionary<string, EpisodeAndLanguage> episodes = new Dictionary<string, EpisodeAndLanguage>(); foreach (var s in parsedSeries.Data){
foreach (int season in result.Keys){
foreach (var key in result[season].Keys){
var s = result[season][key];
if (!string.IsNullOrEmpty(seasonId) && s.Id != seasonId) continue; if (!string.IsNullOrEmpty(seasonId) && s.Id != seasonId) continue;
var sId = s.Id; var sId = s.Id;
if (s.Versions is{ Count: > 0 }){ if (s.Versions is{ Count: > 0 }){
foreach (var sVersion in s.Versions){ foreach (var sVersion in s.Versions.Where(sVersion => sVersion.Original == true)){
if (sVersion.Original == true){ if (sVersion.Guid != null){
if (sVersion.Guid != null){ sId = sVersion.Guid;
sId = sVersion.Guid;
}
break;
} }
break;
} }
} }
var seasonData = await crunInstance.CrSeries.GetSeasonDataById(sId, string.IsNullOrEmpty(crunInstance.CrunOptions.HistoryLang) ? crunInstance.DefaultLocale : crunInstance.CrunOptions.HistoryLang, true); 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){ var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
MatchHistorySeriesWithSonarr(false);
await MatchHistoryEpisodesWithSonarr(false, historySeries); if (historySeries != null){
CfgManager.UpdateHistoryFile(); MatchHistorySeriesWithSonarr(false);
await MatchHistoryEpisodesWithSonarr(false, historySeries);
CfgManager.UpdateHistoryFile();
}
} }
} }
@ -212,116 +207,43 @@ public class History(){
} }
public async Task UpdateWithEpisode(CrunchyEpisode episodeParam){ public async Task UpdateWithSeasonData(List<CrunchyEpisode>? episodeList, bool skippVersionCheck = true){
var episode = episodeParam; if (episodeList != null){
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){
if (!skippVersionCheck){ if (!skippVersionCheck){
if (seasonData.Data.First().Versions != null){ var episodeVersions = episodeList.First().Versions;
var version = seasonData.Data.First().Versions.Find(a => a.Original); if (episodeVersions != null){
if (version.AudioLocale != seasonData.Data.First().AudioLocale){ var version = episodeVersions.Find(a => a.Original);
CRUpdateSeries(seasonData.Data.First().SeriesId, version.SeasonGuid); if (version.AudioLocale != episodeList.First().AudioLocale){
await CRUpdateSeries(episodeList.First().SeriesId, version.SeasonGuid);
return; return;
} }
} else{ } else{
CRUpdateSeries(seasonData.Data.First().SeriesId, ""); await CRUpdateSeries(episodeList.First().SeriesId, "");
return; return;
} }
} }
var firstEpisode = seasonData.Data.First(); var firstEpisode = episodeList.First();
var seriesId = firstEpisode.SeriesId; var seriesId = firstEpisode.SeriesId;
var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId);
if (historySeries != null){ if (historySeries != null){
historySeries.HistorySeriesAddDate ??= DateTime.Now; historySeries.HistorySeriesAddDate ??= DateTime.Now;
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == firstEpisode.SeasonId);
await RefreshSeriesData(seriesId, historySeries); await RefreshSeriesData(seriesId, historySeries);
var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == firstEpisode.SeasonId);
if (historySeason != null){ if (historySeason != null){
historySeason.SeasonTitle = firstEpisode.SeasonTitle; historySeason.SeasonTitle = firstEpisode.SeasonTitle;
historySeason.SeasonNum = Helpers.ExtractNumberAfterS(firstEpisode.Identifier) ?? firstEpisode.SeasonNumber + ""; historySeason.SeasonNum = Helpers.ExtractNumberAfterS(firstEpisode.Identifier) ?? firstEpisode.SeasonNumber + "";
historySeason.SpecialSeason = CheckStringForSpecial(firstEpisode.Identifier); 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); var historyEpisode = historySeason.EpisodesList.Find(e => e.EpisodeId == crunchyEpisode.Id);
if (historyEpisode == null){ if (historyEpisode == null){
var newHistoryEpisode = new HistoryEpisode{ var newHistoryEpisode = new HistoryEpisode{
EpisodeTitle = crunchyEpisode.Identifier.Contains("|M|") ? crunchyEpisode.SeasonTitle : crunchyEpisode.Title, EpisodeTitle = GetEpisodeTitle(crunchyEpisode),
EpisodeDescription = crunchyEpisode.Description, EpisodeDescription = crunchyEpisode.Description,
EpisodeId = crunchyEpisode.Id, EpisodeId = crunchyEpisode.Id,
Episode = crunchyEpisode.Episode, Episode = crunchyEpisode.Episode,
@ -332,7 +254,7 @@ public class History(){
historySeason.EpisodesList.Add(newHistoryEpisode); historySeason.EpisodesList.Add(newHistoryEpisode);
} else{ } else{
//Update existing episode //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.SpecialEpisode = !int.TryParse(crunchyEpisode.Episode, out _);
historyEpisode.EpisodeDescription = crunchyEpisode.Description; historyEpisode.EpisodeDescription = crunchyEpisode.Description;
historyEpisode.EpisodeId = crunchyEpisode.Id; historyEpisode.EpisodeId = crunchyEpisode.Id;
@ -343,7 +265,7 @@ public class History(){
historySeason.EpisodesList.Sort(new NumericStringPropertyComparer()); historySeason.EpisodesList.Sort(new NumericStringPropertyComparer());
} else{ } else{
var newSeason = NewHistorySeason(seasonData, firstEpisode); var newSeason = NewHistorySeason(episodeList, firstEpisode);
newSeason.EpisodesList.Sort(new NumericStringPropertyComparer()); newSeason.EpisodesList.Sort(new NumericStringPropertyComparer());
@ -361,7 +283,7 @@ public class History(){
}; };
crunInstance.HistoryList.Add(historySeries); crunInstance.HistoryList.Add(historySeries);
var newSeason = NewHistorySeason(seasonData, firstEpisode); var newSeason = NewHistorySeason(episodeList, firstEpisode);
newSeason.EpisodesList.Sort(new NumericStringPropertyComparer()); newSeason.EpisodesList.Sort(new NumericStringPropertyComparer());
@ -381,6 +303,34 @@ public class History(){
private CrSeriesBase? cachedSeries; 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){ private async Task RefreshSeriesData(string seriesId, HistorySeries historySeries){
if (cachedSeries == null || (cachedSeries.Data != null && cachedSeries.Data.First().Id != seriesId)){ 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); 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(){ public void SortItems(){
var currentSortingType = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties?.SelectedSorting ?? SortingType.SeriesTitle; var currentSortingType = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties?.SelectedSorting ?? SortingType.SeriesTitle;
var sortingDir = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null && CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending; var sortingDir = CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties != null && CrunchyrollManager.Instance.CrunOptions.HistoryPageProperties.Ascending;
DateTime today = DateTime.UtcNow.Date; DateTime today = DateTime.Now.Date;
switch (currentSortingType){ switch (currentSortingType){
case SortingType.SeriesTitle: case SortingType.SeriesTitle:
var sortedList = sortingDir 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"){ if (dateStr == "Today"){
return today; return today;
} }
@ -502,7 +452,7 @@ public class History(){
return ""; return "";
} }
private static bool CheckStringForSpecial(string identifier){ private bool CheckStringForSpecial(string identifier){
if (string.IsNullOrEmpty(identifier)){ if (string.IsNullOrEmpty(identifier)){
return false; return false;
} }
@ -514,7 +464,7 @@ public class History(){
return Regex.IsMatch(identifier, pattern); return Regex.IsMatch(identifier, pattern);
} }
private static HistorySeason NewHistorySeason(CrunchyEpisodeList seasonData, CrunchyEpisode firstEpisode){ private HistorySeason NewHistorySeason(List<CrunchyEpisode> seasonData, CrunchyEpisode firstEpisode){
var newSeason = new HistorySeason{ var newSeason = new HistorySeason{
SeasonTitle = firstEpisode.SeasonTitle, SeasonTitle = firstEpisode.SeasonTitle,
SeasonId = firstEpisode.SeasonId, SeasonId = firstEpisode.SeasonId,
@ -523,9 +473,9 @@ public class History(){
SpecialSeason = CheckStringForSpecial(firstEpisode.Identifier) SpecialSeason = CheckStringForSpecial(firstEpisode.Identifier)
}; };
foreach (var crunchyEpisode in seasonData.Data!){ foreach (var crunchyEpisode in seasonData){
var newHistoryEpisode = new HistoryEpisode{ var newHistoryEpisode = new HistoryEpisode{
EpisodeTitle = crunchyEpisode.Identifier.Contains("|M|") ? crunchyEpisode.SeasonTitle : crunchyEpisode.Title, EpisodeTitle = GetEpisodeTitle(crunchyEpisode),
EpisodeDescription = crunchyEpisode.Description, EpisodeDescription = crunchyEpisode.Description,
EpisodeId = crunchyEpisode.Id, EpisodeId = crunchyEpisode.Id,
Episode = crunchyEpisode.Episode, Episode = crunchyEpisode.Episode,
@ -539,30 +489,7 @@ public class History(){
return newSeason; return newSeason;
} }
private static HistorySeason NewHistorySeason(CrunchyEpisode episode){ public void MatchHistorySeriesWithSonarr(bool updateAll){
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){
if (crunInstance.CrunOptions.SonarrProperties is{ SonarrEnabled: false }){ if (crunInstance.CrunOptions.SonarrProperties is{ SonarrEnabled: false }){
return; return;
} }
@ -604,7 +531,7 @@ public class History(){
// Create a copy of the episodes list for each thread // Create a copy of the episodes list for each thread
var episodesCopy = new List<SonarrEpisode>(episodes); var episodesCopy = new List<SonarrEpisode>(episodes);
var episode = FindClosestMatchEpisodes(episodesCopy, historyEpisode.EpisodeTitle); var episode = FindClosestMatchEpisodes(episodesCopy, historyEpisode.EpisodeTitle ?? string.Empty);
if (episode != null){ if (episode != null){
historyEpisode.SonarrEpisodeId = episode.Id + ""; historyEpisode.SonarrEpisodeId = episode.Id + "";
historyEpisode.SonarrEpisodeNumber = episode.EpisodeNumber + ""; historyEpisode.SonarrEpisodeNumber = episode.EpisodeNumber + "";

View File

@ -22,6 +22,7 @@ public class CfgManager{
public static readonly string PathCrToken = WorkingDirectory + "/config/cr_token.yml"; public static readonly string PathCrToken = WorkingDirectory + "/config/cr_token.yml";
public static readonly string PathCrDownloadOptions = WorkingDirectory + "/config/settings.yml"; public static readonly string PathCrDownloadOptions = WorkingDirectory + "/config/settings.yml";
public static readonly string PathCrHistory = WorkingDirectory + "/config/history.json"; 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 PathFFMPEG = WorkingDirectory + "/lib/ffmpeg.exe";
public static readonly string PathMKVMERGE = WorkingDirectory + "/lib/mkvmerge.exe"; public static readonly string PathMKVMERGE = WorkingDirectory + "/lib/mkvmerge.exe";

View File

@ -322,7 +322,7 @@ public class HistorySeries : INotifyPropertyChanged{
await CrunchyrollManager.Instance.History.CRUpdateSeries(SeriesId, seasonId); await CrunchyrollManager.Instance.History.CRUpdateSeries(SeriesId, seasonId);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesTitle))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesTitle)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesDescription))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeriesDescription)));
CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, this); // CrunchyrollManager.Instance.History.MatchHistoryEpisodesWithSonarr(false, this);
UpdateNewEpisodes(); UpdateNewEpisodes();
FetchingData = false; FetchingData = false;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData))); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(FetchingData)));

View File

@ -1,4 +1,6 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using CRD.Views;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace CRD.Utils.Structs; namespace CRD.Utils.Structs;
@ -98,3 +100,29 @@ public class FrameData{
public class StringItem{ public class StringItem{
public string stringValue{ get; set; } 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;
}
}

View File

@ -30,18 +30,28 @@ public partial class AccountPageViewModel : ViewModelBase{
private static DispatcherTimer? _timer; private static DispatcherTimer? _timer;
private DateTime _targetTime; private DateTime _targetTime;
private bool IsCancelled = false; private bool IsCancelled = false;
private bool UnknownEndDate = false; private bool UnknownEndDate = false;
private bool EndedButMaybeActive = false;
public AccountPageViewModel(){ public AccountPageViewModel(){
UpdatetProfile(); UpdatetProfile();
} }
private void Timer_Tick(object sender, EventArgs e){ private void Timer_Tick(object sender, EventArgs e){
var remaining = _targetTime - DateTime.UtcNow; var remaining = _targetTime - DateTime.Now;
if (remaining <= TimeSpan.Zero){ if (remaining <= TimeSpan.Zero){
RemainingTime = "No active Subscription"; RemainingTime = "No active Subscription";
_timer.Stop(); _timer.Stop();
if (UnknownEndDate){
RemainingTime = "Unknown Subscription end date";
}
if (EndedButMaybeActive){
RemainingTime = "Subscription maybe ended";
}
if (CrunchyrollManager.Instance.Profile.Subscription != null){ if (CrunchyrollManager.Instance.Profile.Subscription != null){
Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented));
} }
@ -51,25 +61,26 @@ public partial class AccountPageViewModel : ViewModelBase{
} }
public void UpdatetProfile(){ 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 LoginLogoutText = CrunchyrollManager.Instance.Profile.Username == "???" ? "Login" : "Logout"; // Default state
LoadProfileImage("https://static.crunchyroll.com/assets/avatar/170x170/" + CrunchyrollManager.Instance.Profile.Avatar); LoadProfileImage("https://static.crunchyroll.com/assets/avatar/170x170/" + CrunchyrollManager.Instance.Profile.Avatar);
if (CrunchyrollManager.Instance.Profile.Subscription != null && CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts != null){ var subscriptions = CrunchyrollManager.Instance.Profile.Subscription;
if (CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts.Count >= 1){
var sub = CrunchyrollManager.Instance.Profile.Subscription?.SubscriptionProducts.First(); if (subscriptions != null){
if (sub != null){ if (subscriptions.SubscriptionProducts is{ Count: >= 1 }){
IsCancelled = sub.IsCancelled; var sub = subscriptions.SubscriptionProducts.First();
} IsCancelled = sub.IsCancelled;
}else if (CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.Count >= 1){ EndedButMaybeActive = !subscriptions.IsActive;
var sub = CrunchyrollManager.Instance.Profile.Subscription?.ThirdPartySubscriptionProducts.First(); } else if (subscriptions.ThirdPartySubscriptionProducts is{ Count: >= 1 }){
if (sub != null){ var sub = subscriptions.ThirdPartySubscriptionProducts.First();
IsCancelled = !sub.AutoRenew; IsCancelled = !sub.AutoRenew;
} EndedButMaybeActive = !subscriptions.IsActive;
}else if(CrunchyrollManager.Instance.Profile.Subscription?.NonrecurringSubscriptionProducts.Count >= 1){ } else if (subscriptions.NonrecurringSubscriptionProducts is{ Count: >= 1 }){
IsCancelled = true; IsCancelled = true;
}else if(CrunchyrollManager.Instance.Profile.Subscription?.FunimationSubscriptions.Count >= 1){ EndedButMaybeActive = !subscriptions.IsActive;
} else if (subscriptions.FunimationSubscriptions is{ Count: >= 1 }){
IsCancelled = true; IsCancelled = true;
UnknownEndDate = true; UnknownEndDate = true;
} }
@ -82,24 +93,27 @@ public partial class AccountPageViewModel : ViewModelBase{
_timer.Tick += Timer_Tick; _timer.Tick += Timer_Tick;
_timer.Start(); _timer.Start();
} }
} else{ } else{
RemainingTime = "No active Subscription"; RemainingTime = "No active Subscription";
if (_timer != null){ if (_timer != null){
_timer.Stop(); _timer.Stop();
_timer.Tick -= Timer_Tick; _timer.Tick -= Timer_Tick;
} }
RaisePropertyChanged(nameof(RemainingTime)); RaisePropertyChanged(nameof(RemainingTime));
if (CrunchyrollManager.Instance.Profile.Subscription != null){ if (CrunchyrollManager.Instance.Profile.Subscription != null){
Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented)); Console.Error.WriteLine(JsonConvert.SerializeObject(CrunchyrollManager.Instance.Profile.Subscription, Formatting.Indented));
} }
} }
if (UnknownEndDate){ if (UnknownEndDate){
RemainingTime = "Unknown Subscription end date"; RemainingTime = "Unknown Subscription end date";
} }
if (EndedButMaybeActive){
RemainingTime = "Subscription maybe ended";
}
} }
[RelayCommand] [RelayCommand]

View File

@ -1,13 +1,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Platform;
using CRD.Utils;
using CRD.Utils.Structs;
using CRD.Utils.Updater; using CRD.Utils.Updater;
using CRD.ViewModels; using CRD.ViewModels;
using CRD.Views;
using CRD.Views.Utils; using CRD.Views.Utils;
using FluentAvalonia.Core; using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Windowing; using FluentAvalonia.UI.Windowing;
using Newtonsoft.Json;
using ReactiveUI; using ReactiveUI;
using ContentDialogUpdateViewModel = CRD.ViewModels.Utils.ContentDialogUpdateViewModel; using ContentDialogUpdateViewModel = CRD.ViewModels.Utils.ContentDialogUpdateViewModel;
@ -45,6 +52,9 @@ public partial class MainWindow : AppWindow{
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
InitializeComponent(); InitializeComponent();
Opened += OnOpened;
Closing += OnClosing;
TitleBar.ExtendsContentIntoTitleBar = true; TitleBar.ExtendsContentIntoTitleBar = true;
TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex; TitleBar.TitleBarHitTestType = TitleBarHitTestType.Complex;
@ -62,6 +72,7 @@ public partial class MainWindow : AppWindow{
if (viewModel is SeriesPageViewModel){ if (viewModel is SeriesPageViewModel){
((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider); ((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider);
} }
navigationStack.Push(viewModel); navigationStack.Push(viewModel);
nv.Content = viewModel; nv.Content = viewModel;
} else if (!message.Back && message.ViewModelType != null){ } else if (!message.Back && message.ViewModelType != null){
@ -69,6 +80,7 @@ public partial class MainWindow : AppWindow{
if (viewModel is SeriesPageViewModel){ if (viewModel is SeriesPageViewModel){
((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider); ((SeriesPageViewModel)viewModel).SetStorageProvider(StorageProvider);
} }
navigationStack.Push(viewModel); navigationStack.Push(viewModel);
nv.Content = viewModel; nv.Content = viewModel;
} else{ } else{
@ -117,9 +129,10 @@ public partial class MainWindow : AppWindow{
break; break;
case "History": case "History":
navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel)); navView.Content = Activator.CreateInstance(typeof(HistoryPageViewModel));
if ( navView.Content is HistoryPageViewModel){ if (navView.Content is HistoryPageViewModel){
((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider); ((HistoryPageViewModel)navView.Content).SetStorageProvider(StorageProvider);
} }
navigationStack.Clear(); navigationStack.Clear();
navigationStack.Push(navView.Content); navigationStack.Push(navView.Content);
selectedNavVieItem = selectedItem; selectedNavVieItem = selectedItem;
@ -159,22 +172,55 @@ public partial class MainWindow : AppWindow{
_ = await dialog.ShowAsync(); _ = await dialog.ShowAsync();
} }
}
public class ToastMessage(string message, ToastType type, int i){ private void OnOpened(object sender, EventArgs e){
public string? Message{ get; set; } = message; if (File.Exists(CfgManager.PathWindowSettings)){
public int Seconds{ get; set; } = i; var settings = JsonConvert.DeserializeObject<WindowSettings>(File.ReadAllText(CfgManager.PathWindowSettings));
public ToastType Type{ get; set; } = type; if (settings != null){
} Width = settings.Width;
Height = settings.Height;
public class NavigationMessage{ var screens = Screens.All;
public Type? ViewModelType{ get; } if (settings.ScreenIndex >= 0 && settings.ScreenIndex < screens.Count){
public bool Back{ get; } var screen = screens[settings.ScreenIndex];
public bool Refresh{ get; } var screenBounds = screen.Bounds;
public NavigationMessage(Type? viewModelType, bool back, bool refresh){ var topLeft = screenBounds.TopLeft;
ViewModelType = viewModelType; var bottomRight = screenBounds.BottomRight;
Back = back;
Refresh = refresh; 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));
} }
} }