using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CRD.Utils; using CRD.Utils.Ffmpeg_Encoding; using CRD.Utils.Sonarr; using CRD.Utils.Structs; using CRD.Utils.Structs.History; using CRD.ViewModels; using CRD.ViewModels.Utils; using CRD.Views.Utils; using FluentAvalonia.UI.Controls; // ReSharper disable InconsistentNaming namespace CRD.Downloader.Crunchyroll.ViewModels; public partial class CrunchyrollSettingsViewModel : ViewModelBase{ [ObservableProperty] private bool _downloadVideo = true; [ObservableProperty] private bool _downloadAudio = true; [ObservableProperty] private bool _downloadChapters = true; [ObservableProperty] private bool _addScaledBorderAndShadow; [ObservableProperty] private bool _includeSignSubs; [ObservableProperty] private bool _includeCcSubs; [ObservableProperty] private ComboBoxItem _selectedScaledBorderAndShadow; public ObservableCollection ScaledBorderAndShadow{ get; } = new(){ new ComboBoxItem(){ Content = "ScaledBorderAndShadow: yes" }, new ComboBoxItem(){ Content = "ScaledBorderAndShadow: no" }, }; [ObservableProperty] private bool _skipMuxing; [ObservableProperty] private bool _muxToMp4; [ObservableProperty] private bool _syncTimings; [ObservableProperty] private bool _defaultSubSigns; [ObservableProperty] private bool _defaultSubForcedDisplay; [ObservableProperty] private bool _includeEpisodeDescription; [ObservableProperty] private bool _downloadVideoForEveryDub; [ObservableProperty] private bool _keepDubsSeparate; [ObservableProperty] private bool _skipSubMux; [ObservableProperty] private double? _leadingNumbers; [ObservableProperty] private double? _partSize; [ObservableProperty] private string _fileName = ""; [ObservableProperty] private string _fileTitle = ""; [ObservableProperty] private ObservableCollection _mkvMergeOptions = new(); [ObservableProperty] private string _mkvMergeOption = ""; [ObservableProperty] private string _ffmpegOption = ""; [ObservableProperty] private ObservableCollection _ffmpegOptions = new(); [ObservableProperty] private string _selectedSubs = "all"; [ObservableProperty] private ComboBoxItem _selectedHSLang; [ObservableProperty] private ComboBoxItem _selectedDescriptionLang; [ObservableProperty] private string _selectedDubs = "ja-JP"; [ObservableProperty] private ObservableCollection _selectedDubLang = new(); [ObservableProperty] private ComboBoxItem _selectedStreamEndpoint; [ObservableProperty] private ComboBoxItem _selectedDefaultDubLang; [ObservableProperty] private ComboBoxItem _selectedDefaultSubLang; [ObservableProperty] private ComboBoxItem? _selectedVideoQuality; [ObservableProperty] private ComboBoxItem? _selectedAudioQuality; [ObservableProperty] private ObservableCollection _selectedSubLang = new(); [ObservableProperty] private Color _listBoxColor; public ObservableCollection VideoQualityList{ get; } = new(){ new ComboBoxItem(){ Content = "best" }, new ComboBoxItem(){ Content = "1080" }, new ComboBoxItem(){ Content = "720" }, new ComboBoxItem(){ Content = "480" }, new ComboBoxItem(){ Content = "360" }, new ComboBoxItem(){ Content = "240" }, new ComboBoxItem(){ Content = "worst" }, }; public ObservableCollection AudioQualityList{ get; } = new(){ new ComboBoxItem(){ Content = "best" }, new ComboBoxItem(){ Content = "128kB/s" }, new ComboBoxItem(){ Content = "96kB/s" }, new ComboBoxItem(){ Content = "64kB/s" }, new ComboBoxItem(){ Content = "worst" }, }; public ObservableCollection HardSubLangList{ get; } = new(){ new ComboBoxItem(){ Content = "none" }, }; public ObservableCollection DescriptionLangList{ get; } = new(){ new ComboBoxItem(){ Content = "default" }, new ComboBoxItem(){ Content = "de-DE" }, new ComboBoxItem(){ Content = "en-US" }, new ComboBoxItem(){ Content = "es-419" }, new ComboBoxItem(){ Content = "es-ES" }, new ComboBoxItem(){ Content = "fr-FR" }, new ComboBoxItem(){ Content = "it-IT" }, new ComboBoxItem(){ Content = "pt-BR" }, new ComboBoxItem(){ Content = "pt-PT" }, new ComboBoxItem(){ Content = "ru-RU" }, new ComboBoxItem(){ Content = "hi-IN" }, new ComboBoxItem(){ Content = "ar-SA" }, }; public ObservableCollection DubLangList{ get; } = new(){ }; public ObservableCollection DefaultDubLangList{ get; } = new(){ }; public ObservableCollection DefaultSubLangList{ get; } = new(){ }; public ObservableCollection SubLangList{ get; } = new(){ new ListBoxItem(){ Content = "all" }, new ListBoxItem(){ Content = "none" }, }; public ObservableCollection StreamEndpoints{ get; } = new(){ new ComboBoxItem(){ Content = "web/firefox" }, new ComboBoxItem(){ Content = "console/switch" }, new ComboBoxItem(){ Content = "console/ps4" }, new ComboBoxItem(){ Content = "console/ps5" }, new ComboBoxItem(){ Content = "console/xbox_one" }, new ComboBoxItem(){ Content = "web/edge" }, // new ComboBoxItem(){ Content = "web/safari" }, new ComboBoxItem(){ Content = "web/chrome" }, new ComboBoxItem(){ Content = "web/fallback" }, // new ComboBoxItem(){ Content = "ios/iphone" }, // new ComboBoxItem(){ Content = "ios/ipad" }, new ComboBoxItem(){ Content = "android/phone" }, new ComboBoxItem(){ Content = "tv/samsung" }, }; [ObservableProperty] private bool _isEncodeEnabled; [ObservableProperty] private StringItem _selectedEncodingPreset; public ObservableCollection EncodingPresetsList{ get; } = new(); [ObservableProperty] private bool _cCSubsMuxingFlag; [ObservableProperty] private string _cCSubsFont; [ObservableProperty] private bool _signsSubsAsForced; private bool settingsLoaded; public CrunchyrollSettingsViewModel(){ foreach (var languageItem in Languages.languages){ HardSubLangList.Add(new ComboBoxItem{ Content = languageItem.CrLocale }); SubLangList.Add(new ListBoxItem{ Content = languageItem.CrLocale }); DubLangList.Add(new ListBoxItem{ Content = languageItem.CrLocale }); DefaultDubLangList.Add(new ComboBoxItem{ Content = languageItem.CrLocale }); DefaultSubLangList.Add(new ComboBoxItem{ Content = languageItem.CrLocale }); } foreach (var encodingPreset in FfmpegEncoding.presets){ EncodingPresetsList.Add(new StringItem{ stringValue = encodingPreset.PresetName ?? "Unknown Preset Name" }); } CrDownloadOptions options = CrunchyrollManager.Instance.CrunOptions; StringItem? encodingPresetSelected = EncodingPresetsList.FirstOrDefault(a => a.stringValue != null && a.stringValue == options.EncodingPresetName) ?? null; SelectedEncodingPreset = encodingPresetSelected ?? EncodingPresetsList[0]; ComboBoxItem? descriptionLang = DescriptionLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.DescriptionLang) ?? null; SelectedDescriptionLang = descriptionLang ?? DescriptionLangList[0]; ComboBoxItem? hsLang = HardSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == Languages.Locale2language(options.Hslang).CrLocale) ?? null; SelectedHSLang = hsLang ?? HardSubLangList[0]; ComboBoxItem? defaultDubLang = DefaultDubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.DefaultAudio ?? "")) ?? null; SelectedDefaultDubLang = defaultDubLang ?? DefaultDubLangList[0]; ComboBoxItem? defaultSubLang = DefaultSubLangList.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.DefaultSub ?? "")) ?? null; SelectedDefaultSubLang = defaultSubLang ?? DefaultSubLangList[0]; ComboBoxItem? streamEndpoint = StreamEndpoints.FirstOrDefault(a => a.Content != null && (string)a.Content == (options.StreamEndpoint ?? "")) ?? null; SelectedStreamEndpoint = streamEndpoint ?? StreamEndpoints[0]; var softSubLang = SubLangList.Where(a => options.DlSubs.Contains(a.Content)).ToList(); SelectedSubLang.Clear(); foreach (var listBoxItem in softSubLang){ SelectedSubLang.Add(listBoxItem); } var dubLang = DubLangList.Where(a => options.DubLang.Contains(a.Content)).ToList(); SelectedDubLang.Clear(); foreach (var listBoxItem in dubLang){ SelectedDubLang.Add(listBoxItem); } AddScaledBorderAndShadow = options.SubsAddScaledBorder is ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo or ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes; SelectedScaledBorderAndShadow = GetScaledBorderAndShadowFromOptions(options); CCSubsFont = options.CcSubsFont ?? ""; CCSubsMuxingFlag = options.CcSubsMuxingFlag; SignsSubsAsForced = options.SignsSubsAsForced; SkipMuxing = options.SkipMuxing; IsEncodeEnabled = options.IsEncodeEnabled; DefaultSubForcedDisplay = options.DefaultSubForcedDisplay; DefaultSubSigns = options.DefaultSubSigns; PartSize = options.Partsize; IncludeEpisodeDescription = options.IncludeVideoDescription; FileTitle = options.VideoTitle ?? ""; IncludeSignSubs = options.IncludeSignsSubs; IncludeCcSubs = options.IncludeCcSubs; DownloadVideo = !options.Novids; DownloadAudio = !options.Noaudio; DownloadVideoForEveryDub = !options.DlVideoOnce; KeepDubsSeparate = options.KeepDubsSeperate; DownloadChapters = options.Chapters; MuxToMp4 = options.Mp4; SyncTimings = options.SyncTiming; SkipSubMux = options.SkipSubsMux; LeadingNumbers = options.Numbers; FileName = options.FileName; ComboBoxItem? qualityAudio = AudioQualityList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.QualityAudio) ?? null; SelectedAudioQuality = qualityAudio ?? AudioQualityList[0]; ComboBoxItem? qualityVideo = VideoQualityList.FirstOrDefault(a => a.Content != null && (string)a.Content == options.QualityVideo) ?? null; SelectedVideoQuality = qualityVideo ?? VideoQualityList[0]; MkvMergeOptions.Clear(); if (options.MkvmergeOptions != null){ foreach (var mkvmergeParam in options.MkvmergeOptions){ MkvMergeOptions.Add(new StringItem(){ stringValue = mkvmergeParam }); } } FfmpegOptions.Clear(); if (options.FfmpegOptions != null){ foreach (var ffmpegParam in options.FfmpegOptions){ FfmpegOptions.Add(new StringItem(){ stringValue = ffmpegParam }); } } var dubs = SelectedDubLang.Select(item => item.Content?.ToString()); SelectedDubs = string.Join(", ", dubs) ?? ""; var subs = SelectedSubLang.Select(item => item.Content?.ToString()); SelectedSubs = string.Join(", ", subs) ?? ""; SelectedSubLang.CollectionChanged += Changes; SelectedDubLang.CollectionChanged += Changes; MkvMergeOptions.CollectionChanged += Changes; FfmpegOptions.CollectionChanged += Changes; settingsLoaded = true; } private void UpdateSettings(){ if (!settingsLoaded){ return; } CrunchyrollManager.Instance.CrunOptions.SignsSubsAsForced = SignsSubsAsForced; CrunchyrollManager.Instance.CrunOptions.CcSubsMuxingFlag = CCSubsMuxingFlag; CrunchyrollManager.Instance.CrunOptions.CcSubsFont = CCSubsFont; CrunchyrollManager.Instance.CrunOptions.EncodingPresetName = SelectedEncodingPreset.stringValue; CrunchyrollManager.Instance.CrunOptions.IsEncodeEnabled = IsEncodeEnabled; CrunchyrollManager.Instance.CrunOptions.DefaultSubSigns = DefaultSubSigns; CrunchyrollManager.Instance.CrunOptions.DefaultSubForcedDisplay = DefaultSubForcedDisplay; CrunchyrollManager.Instance.CrunOptions.IncludeVideoDescription = IncludeEpisodeDescription; CrunchyrollManager.Instance.CrunOptions.VideoTitle = FileTitle; CrunchyrollManager.Instance.CrunOptions.Novids = !DownloadVideo; CrunchyrollManager.Instance.CrunOptions.Noaudio = !DownloadAudio; CrunchyrollManager.Instance.CrunOptions.DlVideoOnce = !DownloadVideoForEveryDub; CrunchyrollManager.Instance.CrunOptions.KeepDubsSeperate = KeepDubsSeparate; CrunchyrollManager.Instance.CrunOptions.Chapters = DownloadChapters; CrunchyrollManager.Instance.CrunOptions.SkipMuxing = SkipMuxing; CrunchyrollManager.Instance.CrunOptions.Mp4 = MuxToMp4; CrunchyrollManager.Instance.CrunOptions.SyncTiming = SyncTimings; CrunchyrollManager.Instance.CrunOptions.SkipSubsMux = SkipSubMux; CrunchyrollManager.Instance.CrunOptions.Numbers = Math.Clamp((int)(LeadingNumbers ?? 0), 0, 10); CrunchyrollManager.Instance.CrunOptions.FileName = FileName; CrunchyrollManager.Instance.CrunOptions.IncludeSignsSubs = IncludeSignSubs; CrunchyrollManager.Instance.CrunOptions.IncludeCcSubs = IncludeCcSubs; CrunchyrollManager.Instance.CrunOptions.Partsize = Math.Clamp((int)(PartSize ?? 0), 0, 10000); CrunchyrollManager.Instance.CrunOptions.SubsAddScaledBorder = GetScaledBorderAndShadowSelection(); List softSubs = new List(); foreach (var listBoxItem in SelectedSubLang){ softSubs.Add(listBoxItem.Content + ""); } CrunchyrollManager.Instance.CrunOptions.DlSubs = softSubs; string descLang = SelectedDescriptionLang.Content + ""; CrunchyrollManager.Instance.CrunOptions.DescriptionLang = descLang != "default" ? descLang : CrunchyrollManager.Instance.DefaultLocale; string hslang = SelectedHSLang.Content + ""; CrunchyrollManager.Instance.CrunOptions.Hslang = hslang != "none" ? Languages.FindLang(hslang).Locale : hslang; CrunchyrollManager.Instance.CrunOptions.DefaultAudio = SelectedDefaultDubLang.Content + ""; CrunchyrollManager.Instance.CrunOptions.DefaultSub = SelectedDefaultSubLang.Content + ""; CrunchyrollManager.Instance.CrunOptions.StreamEndpoint = SelectedStreamEndpoint.Content + ""; List dubLangs = new List(); foreach (var listBoxItem in SelectedDubLang){ dubLangs.Add(listBoxItem.Content + ""); } CrunchyrollManager.Instance.CrunOptions.DubLang = dubLangs; CrunchyrollManager.Instance.CrunOptions.QualityAudio = SelectedAudioQuality?.Content + ""; CrunchyrollManager.Instance.CrunOptions.QualityVideo = SelectedVideoQuality?.Content + ""; List mkvmergeParams = new List(); foreach (var mkvmergeParam in MkvMergeOptions){ mkvmergeParams.Add(mkvmergeParam.stringValue); } CrunchyrollManager.Instance.CrunOptions.MkvmergeOptions = mkvmergeParams; List ffmpegParams = new List(); foreach (var ffmpegParam in FfmpegOptions){ ffmpegParams.Add(ffmpegParam.stringValue); } CrunchyrollManager.Instance.CrunOptions.FfmpegOptions = ffmpegParams; CfgManager.WriteSettingsToFile(); } private ScaledBorderAndShadowSelection GetScaledBorderAndShadowSelection(){ if (!AddScaledBorderAndShadow){ return ScaledBorderAndShadowSelection.DontAdd; } if (SelectedScaledBorderAndShadow.Content + "" == "ScaledBorderAndShadow: yes"){ return ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes; } if (SelectedScaledBorderAndShadow.Content + "" == "ScaledBorderAndShadow: no"){ return ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo; } return ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes; } private ComboBoxItem GetScaledBorderAndShadowFromOptions(CrDownloadOptions options){ switch (options.SubsAddScaledBorder){ case (ScaledBorderAndShadowSelection.ScaledBorderAndShadowYes): return ScaledBorderAndShadow.FirstOrDefault(a => a.Content != null && (string)a.Content == "ScaledBorderAndShadow: yes") ?? ScaledBorderAndShadow[0]; case ScaledBorderAndShadowSelection.ScaledBorderAndShadowNo: return ScaledBorderAndShadow.FirstOrDefault(a => a.Content != null && (string)a.Content == "ScaledBorderAndShadow: no") ?? ScaledBorderAndShadow[0]; default: return ScaledBorderAndShadow[0]; } } [RelayCommand] public void AddMkvMergeParam(){ MkvMergeOptions.Add(new StringItem(){ stringValue = MkvMergeOption }); MkvMergeOption = ""; RaisePropertyChanged(nameof(MkvMergeOptions)); } [RelayCommand] public void RemoveMkvMergeParam(StringItem param){ MkvMergeOptions.Remove(param); RaisePropertyChanged(nameof(MkvMergeOptions)); } [RelayCommand] public void AddFfmpegParam(){ FfmpegOptions.Add(new StringItem(){ stringValue = FfmpegOption }); FfmpegOption = ""; RaisePropertyChanged(nameof(FfmpegOptions)); } [RelayCommand] public void RemoveFfmpegParam(StringItem param){ FfmpegOptions.Remove(param); RaisePropertyChanged(nameof(FfmpegOptions)); } private void Changes(object? sender, NotifyCollectionChangedEventArgs e){ UpdateSettings(); var dubs = SelectedDubLang.Select(item => item.Content?.ToString()); SelectedDubs = string.Join(", ", dubs) ?? ""; var subs = SelectedSubLang.Select(item => item.Content?.ToString()); SelectedSubs = string.Join(", ", subs) ?? ""; } protected override void OnPropertyChanged(PropertyChangedEventArgs e){ base.OnPropertyChanged(e); if (e.PropertyName is nameof(SelectedDubs) or nameof(SelectedSubs) or nameof(ListBoxColor)){ return; } UpdateSettings(); if (e.PropertyName is nameof(History)){ if (CrunchyrollManager.Instance.CrunOptions.History){ if (File.Exists(CfgManager.PathCrHistory)){ var decompressedJson = CfgManager.DecompressJsonFile(CfgManager.PathCrHistory); if (!string.IsNullOrEmpty(decompressedJson)){ CrunchyrollManager.Instance.HistoryList = Helpers.Deserialize>(decompressedJson, CrunchyrollManager.Instance.SettingsJsonSerializerSettings) ?? new ObservableCollection(); foreach (var historySeries in CrunchyrollManager.Instance.HistoryList){ historySeries.Init(); foreach (var historySeriesSeason in historySeries.Seasons){ historySeriesSeason.Init(); } } } else{ CrunchyrollManager.Instance.HistoryList =[]; } } _ = SonarrClient.Instance.RefreshSonarrLite(); } else{ CrunchyrollManager.Instance.HistoryList =[]; } } } [RelayCommand] public async Task CreateEncodingPresetButtonPress(bool editMode){ var dialog = new ContentDialog(){ Title = "New Encoding Preset", PrimaryButtonText = "Save", CloseButtonText = "Close", FullSizeDesired = true }; var viewModel = new ContentDialogEncodingPresetViewModel(dialog, editMode); dialog.Content = new ContentDialogEncodingPresetView(){ DataContext = viewModel }; var dialogResult = await dialog.ShowAsync(); if (dialogResult == ContentDialogResult.Primary){ settingsLoaded = false; EncodingPresetsList.Clear(); foreach (var encodingPreset in FfmpegEncoding.presets){ EncodingPresetsList.Add(new StringItem{ stringValue = encodingPreset.PresetName ?? "Unknown Preset Name" }); } settingsLoaded = true; StringItem? encodingPresetSelected = EncodingPresetsList.FirstOrDefault(a => a.stringValue != null && a.stringValue == CrunchyrollManager.Instance.CrunOptions.EncodingPresetName) ?? null; SelectedEncodingPreset = encodingPresetSelected ?? EncodingPresetsList[0]; } } }