diff --git a/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs index d70025a..d3268fe 100644 --- a/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs +++ b/CRD/Downloader/Crunchyroll/CrunchyrollManager.cs @@ -1402,17 +1402,29 @@ public class CrunchyrollManager{ assBuilder.AppendLine("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"); // Parse the VTT content - var lines = subsAssReqResponse.ResponseContent.Split(new[]{ Environment.NewLine }, StringSplitOptions.None); + string normalizedContent = subsAssReqResponse.ResponseContent.Replace("\r\n", "\n").Replace("\r", "\n"); + var blocks = normalizedContent.Split(new[]{ "\n\n" }, StringSplitOptions.RemoveEmptyEntries); Regex timePattern = new Regex(@"(?\d{2}:\d{2}:\d{2}\.\d{3})\s-->\s(?\d{2}:\d{2}:\d{2}\.\d{3})"); - for (int i = 0; i < lines.Length; i++){ - Match match = timePattern.Match(lines[i]); + foreach (var block in blocks){ + // Split each block into lines + var lines = block.Split(new[]{ '\n' }, StringSplitOptions.RemoveEmptyEntries); + + if (lines.Length < 3) continue; // Skip blocks that don't have enough lines + + // Match the first line to get the time codes + Match match = timePattern.Match(lines[1]); if (match.Success){ string startTime = Helpers.ConvertTimeFormat(match.Groups["start"].Value); string endTime = Helpers.ConvertTimeFormat(match.Groups["end"].Value); - string dialogue = Helpers.ExtractDialogue(lines, i + 1); + // Join the remaining lines for dialogue, using \N for line breaks + string dialogue = string.Join("\\N", lines.Skip(2)); + + dialogue = Helpers.ConvertVTTStylesToASS(dialogue); + + // Append dialogue to ASS assBuilder.AppendLine($"Dialogue: 0,{startTime},{endTime},Default,,0000,0000,0000,,{dialogue}"); } } diff --git a/CRD/Downloader/History.cs b/CRD/Downloader/History.cs index 37dae84..ff39bab 100644 --- a/CRD/Downloader/History.cs +++ b/CRD/Downloader/History.cs @@ -65,7 +65,6 @@ public class History(){ } - public void SetAsDownloaded(string? seriesId, string? seasonId, string episodeId){ var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); @@ -129,18 +128,24 @@ public class History(){ return (null, downloadDirPath); } - - public (HistoryEpisode? historyEpisode, List dublist, string downloadDirPath) GetHistoryEpisodeWithDubListAndDownloadDir(string? seriesId, string? seasonId, string episodeId){ + + public (HistoryEpisode? historyEpisode, List dublist, List sublist, string downloadDirPath) GetHistoryEpisodeWithDubListAndDownloadDir(string? seriesId, string? seasonId, string episodeId){ var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); var downloadDirPath = ""; - List dublist = []; + List dublist =[]; + List sublist =[]; if (historySeries != null){ var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId); if (historySeries.HistorySeriesDubLangOverride.Count > 0){ dublist = historySeries.HistorySeriesDubLangOverride; } + + if (historySeries.HistorySeriesSoftSubsOverride.Count > 0){ + sublist = historySeries.HistorySeriesSoftSubsOverride; + } + if (!string.IsNullOrEmpty(historySeries.SeriesDownloadPath)){ downloadDirPath = historySeries.SeriesDownloadPath; } @@ -150,23 +155,28 @@ public class History(){ if (historySeason.HistorySeasonDubLangOverride.Count > 0){ dublist = historySeason.HistorySeasonDubLangOverride; } + + if (historySeason.HistorySeasonSoftSubsOverride.Count > 0){ + sublist = historySeason.HistorySeasonSoftSubsOverride; + } + if (!string.IsNullOrEmpty(historySeason.SeasonDownloadPath)){ downloadDirPath = historySeason.SeasonDownloadPath; } if (historyEpisode != null){ - return (historyEpisode, dublist,downloadDirPath); + return (historyEpisode, dublist, sublist, downloadDirPath); } } } - return (null, dublist,downloadDirPath); + return (null, dublist, sublist, downloadDirPath); } - + public List GetDubList(string? seriesId, string? seasonId){ var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); - - List dublist = []; + + List dublist =[]; if (historySeries != null){ var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId); @@ -181,7 +191,25 @@ public class History(){ return dublist; } - + + public List GetSubList(string? seriesId, string? seasonId){ + var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); + + List sublist =[]; + + if (historySeries != null){ + var historySeason = historySeries.Seasons.FirstOrDefault(s => s.SeasonId == seasonId); + if (historySeries.HistorySeriesSoftSubsOverride.Count > 0){ + sublist = historySeries.HistorySeriesSoftSubsOverride; + } + + if (historySeason is{ HistorySeasonSoftSubsOverride.Count: > 0 }){ + sublist = historySeason.HistorySeasonSoftSubsOverride; + } + } + + return sublist; + } public async Task UpdateWithEpisode(CrunchyEpisode episodeParam){ @@ -256,13 +284,10 @@ public class History(){ SortItems(); SortSeasons(historySeries); - - } - public async Task UpdateWithSeasonData(CrunchyEpisodeList seasonData,bool skippVersionCheck = true){ + public async Task UpdateWithSeasonData(CrunchyEpisodeList seasonData, bool skippVersionCheck = true){ if (seasonData.Data != null){ - if (!skippVersionCheck){ if (seasonData.Data.First().Versions != null){ var version = seasonData.Data.First().Versions.Find(a => a.Original); @@ -275,8 +300,8 @@ public class History(){ return; } } - - + + var firstEpisode = seasonData.Data.First(); var seriesId = firstEpisode.SeriesId; var historySeries = crunInstance.HistoryList.FirstOrDefault(series => series.SeriesId == seriesId); @@ -471,7 +496,7 @@ public class History(){ if (string.IsNullOrEmpty(identifier)){ return false; } - + // Regex pattern to match any sequence that does NOT contain "|S" followed by one or more digits immediately after string pattern = @"^(?!.*\|S\d+).*"; @@ -545,7 +570,7 @@ public class History(){ } private static readonly object _lock = new object(); - + public async Task MatchHistoryEpisodesWithSonarr(bool updateAll, HistorySeries historySeries){ if (crunInstance.CrunOptions.SonarrProperties is{ SonarrEnabled: false }){ return; @@ -576,11 +601,11 @@ public class History(){ historyEpisode.SonarrHasFile = episode.HasFile; historyEpisode.SonarrAbsolutNumber = episode.AbsoluteEpisodeNumber + ""; historyEpisode.SonarrSeasonNumber = episode.SeasonNumber + ""; - lock (_lock) { + lock (_lock){ episodes.Remove(episode); } } else{ - lock (_lock) { + lock (_lock){ failedEpisodes.Add(historyEpisode); } } @@ -604,7 +629,7 @@ public class History(){ historyEpisode.SonarrHasFile = episode.HasFile; historyEpisode.SonarrAbsolutNumber = episode.AbsoluteEpisodeNumber + ""; historyEpisode.SonarrSeasonNumber = episode.SeasonNumber + ""; - lock (_lock) { + lock (_lock){ episodes.Remove(episode); } } else{ @@ -622,7 +647,7 @@ public class History(){ historyEpisode.SonarrHasFile = episode1.HasFile; historyEpisode.SonarrAbsolutNumber = episode1.AbsoluteEpisodeNumber + ""; historyEpisode.SonarrSeasonNumber = episode1.SeasonNumber + ""; - lock (_lock) { + lock (_lock){ episodes.Remove(episode1); } } else{ @@ -639,7 +664,7 @@ public class History(){ historyEpisode.SonarrHasFile = episode2.HasFile; historyEpisode.SonarrAbsolutNumber = episode2.AbsoluteEpisodeNumber + ""; historyEpisode.SonarrSeasonNumber = episode2.SeasonNumber + ""; - lock (_lock) { + lock (_lock){ episodes.Remove(episode2); } } else{ @@ -648,9 +673,8 @@ public class History(){ } } }); - + CfgManager.UpdateHistoryFile(); - } } @@ -712,7 +736,7 @@ public class History(){ return highestSimilarity < 0.8 ? null : closestMatch; } - + public CrBrowseSeries? FindClosestMatchCrSeries(List episodeList, string title){ CrBrowseSeries? closestMatch = null; double highestSimilarity = 0.0; diff --git a/CRD/Downloader/QueueManager.cs b/CRD/Downloader/QueueManager.cs index 9e44b85..1b130d2 100644 --- a/CRD/Downloader/QueueManager.cs +++ b/CRD/Downloader/QueueManager.cs @@ -100,7 +100,7 @@ public class QueueManager{ var sList = await CrunchyrollManager.Instance.CrEpisode.EpisodeData((CrunchyEpisode)episodeL, updateHistory); - (HistoryEpisode? historyEpisode, List dublist, string downloadDirPath) historyEpisode = (null, [], ""); + (HistoryEpisode? historyEpisode, List dublist, List sublist, string downloadDirPath) historyEpisode = (null, [], [], ""); if (CrunchyrollManager.Instance.CrunOptions.History){ var episode = sList.EpisodeAndLanguages.Items.First(); @@ -141,7 +141,8 @@ public class QueueManager{ } } - selected.DownloadSubs = CrunchyrollManager.Instance.CrunOptions.DlSubs; + selected.DownloadSubs = historyEpisode.sublist.Count > 0 ? historyEpisode.sublist : CrunchyrollManager.Instance.CrunOptions.DlSubs; + Queue.Add(selected); @@ -175,12 +176,12 @@ public class QueueManager{ } } - + public void CrAddEpMetaToQueue(CrunchyEpMeta epMeta){ - Queue.Add(epMeta); - MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue", ToastType.Information, 1)); + Queue.Add(epMeta); + MessageBus.Current.SendMessage(new ToastMessage($"Added episode to the queue", ToastType.Information, 1)); } - + public async Task CrAddMusicVideoToQueue(string epId){ await CrunchyrollManager.Instance.CrAuth.RefreshToken(true); @@ -191,13 +192,11 @@ public class QueueManager{ Queue.Add(musicVideoMeta); MessageBus.Current.SendMessage(new ToastMessage($"Added music video to the queue", ToastType.Information, 1)); } - - } public async Task CrAddConcertToQueue(string epId){ await CrunchyrollManager.Instance.CrAuth.RefreshToken(true); - + var concert = await CrunchyrollManager.Instance.CrMusic.ParseConcertByIdAsync(epId, ""); if (concert != null){ @@ -205,7 +204,6 @@ public class QueueManager{ Queue.Add(concertMeta); MessageBus.Current.SendMessage(new ToastMessage($"Added concert to the queue", ToastType.Information, 1)); } - } @@ -213,7 +211,7 @@ public class QueueManager{ var selected = CrunchyrollManager.Instance.CrSeries.ItemSelectMultiDub(list.Data, data.DubLang, data.But, data.AllEpisodes, data.E); bool failed = false; - + foreach (var crunchyEpMeta in selected.Values.ToList()){ if (crunchyEpMeta.Data?.First().Playback != null){ if (CrunchyrollManager.Instance.CrunOptions.History){ @@ -243,7 +241,10 @@ public class QueueManager{ } } - crunchyEpMeta.DownloadSubs = CrunchyrollManager.Instance.CrunOptions.DlSubs; + var subLangList = CrunchyrollManager.Instance.History.GetSubList(crunchyEpMeta.ShowId, crunchyEpMeta.SeasonId); + crunchyEpMeta.DownloadSubs = subLangList.Count > 0 ? subLangList : CrunchyrollManager.Instance.CrunOptions.DlSubs; + + Queue.Add(crunchyEpMeta); } else{ failed = true; diff --git a/CRD/Utils/Helpers.cs b/CRD/Utils/Helpers.cs index 659f839..9905ca7 100644 --- a/CRD/Utils/Helpers.cs +++ b/CRD/Utils/Helpers.cs @@ -42,6 +42,19 @@ public class Helpers{ return $"{hours}:{minutes:D2}:{seconds:D2}.{milliseconds / 10:D2}"; } + public static string ConvertVTTStylesToASS(string dialogue){ + dialogue = Regex.Replace(dialogue, @"", "{\\b1}"); + dialogue = Regex.Replace(dialogue, @"", "{\\b0}"); + dialogue = Regex.Replace(dialogue, @"", "{\\i1}"); + dialogue = Regex.Replace(dialogue, @"", "{\\i0}"); + dialogue = Regex.Replace(dialogue, @"", "{\\u1}"); + dialogue = Regex.Replace(dialogue, @"", "{\\u0}"); + + dialogue = Regex.Replace(dialogue, @"<[^>]+>", ""); // Remove any other HTML-like tags + + return dialogue; + } + public static string ExtractDialogue(string[] lines, int startLine){ var dialogueBuilder = new StringBuilder(); @@ -410,7 +423,4 @@ public class Helpers{ return languageGroups; } - - - } \ No newline at end of file diff --git a/CRD/Views/SettingsPageView.axaml b/CRD/Views/SettingsPageView.axaml index 39c8b19..b253cf4 100644 --- a/CRD/Views/SettingsPageView.axaml +++ b/CRD/Views/SettingsPageView.axaml @@ -301,7 +301,7 @@ Description="MKVMerge and FFMpeg Settings" IsExpanded="False"> - +