diff --git a/OF DL.Gui/Services/ConfigValidationService.cs b/OF DL.Gui/Services/ConfigValidationService.cs index bf83c89..2374a3e 100644 --- a/OF DL.Gui/Services/ConfigValidationService.cs +++ b/OF DL.Gui/Services/ConfigValidationService.cs @@ -10,6 +10,7 @@ internal static class ConfigValidationService ValidatePath(config.DownloadPath, nameof(Config.DownloadPath), errors, requireExistingFile: false); ValidatePath(config.FFmpegPath, nameof(Config.FFmpegPath), errors, requireExistingFile: true); + ValidatePath(config.FFprobePath, nameof(Config.FFprobePath), errors, requireExistingFile: true); if (config.Timeout.HasValue && config.Timeout.Value <= 0 && config.Timeout.Value != -1) { diff --git a/OF DL.Gui/ViewModels/MainWindowViewModel.cs b/OF DL.Gui/ViewModels/MainWindowViewModel.cs index 5ac9b96..150908e 100644 --- a/OF DL.Gui/ViewModels/MainWindowViewModel.cs +++ b/OF DL.Gui/ViewModels/MainWindowViewModel.cs @@ -49,8 +49,12 @@ public partial class MainWindowViewModel( { [nameof(Config.FFmpegPath)] = "Path to the FFmpeg executable. If blank, OF-DL will try the app directory and PATH.", + [nameof(Config.FFprobePath)] = + "Path to the FFprobe executable. If blank, OF-DL will try FFmpeg's directory, the app directory, and PATH.", [nameof(Config.DownloadPath)] = "Base download folder. If blank, OF-DL uses __user_data__/sites/OnlyFans/{username}.", + [nameof(Config.DrmVideoDurationMatchThreshold)] = + "Minimum DRM video duration match threshold. Higher values are stricter. 100% requires an exact duration match. 98% is the recommended value.", [nameof(Config.DownloadVideos)] = "Download video media when enabled.", [nameof(Config.DownloadImages)] = "Download image media when enabled.", [nameof(Config.DownloadAudios)] = "Download audio media when enabled.", @@ -164,6 +168,7 @@ public partial class MainWindowViewModel( [ObservableProperty] private string _errorMessage = string.Empty; private string _actualFfmpegPath = string.Empty; + private string _actualFfprobePath = string.Empty; private string _actualDownloadPath = string.Empty; [ObservableProperty] [NotifyPropertyChangedFor(nameof(FfmpegPathDisplay))] @@ -172,12 +177,21 @@ public partial class MainWindowViewModel( [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfmpegPathError))] private string _ffmpegPathError = string.Empty; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfprobePathError))] + private string _ffprobePath = string.Empty; + + [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfprobePathError))] + private string _ffprobePathError = string.Empty; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(DownloadPathDisplay))] private string _downloadPath = string.Empty; [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasDownloadPathError))] private string _downloadPathError = string.Empty; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(DrmVideoDurationMatchThresholdPercentLabel))] + private double _drmVideoDurationMatchThresholdPercent = 98; + [ObservableProperty] [NotifyPropertyChangedFor(nameof(HasMediaTypesError))] private string _mediaTypesError = string.Empty; @@ -218,6 +232,8 @@ public partial class MainWindowViewModel( public bool HasFfmpegPathError => !string.IsNullOrWhiteSpace(FfmpegPathError); + public bool HasFfprobePathError => !string.IsNullOrWhiteSpace(FfprobePathError); + public bool HasDownloadPathError => !string.IsNullOrWhiteSpace(DownloadPathError); public bool HasMediaTypesError => !string.IsNullOrWhiteSpace(MediaTypesError); @@ -233,8 +249,16 @@ public partial class MainWindowViewModel( public string FfmpegPathHelpText => GetConfigHelpText(nameof(Config.FFmpegPath)); + public string FfprobePathHelpText => GetConfigHelpText(nameof(Config.FFprobePath)); + public string DownloadPathHelpText => GetConfigHelpText(nameof(Config.DownloadPath)); + public string DrmVideoDurationMatchThresholdHelpText => + GetConfigHelpText(nameof(Config.DrmVideoDurationMatchThreshold)); + + public string DrmVideoDurationMatchThresholdPercentLabel => + $"{Math.Round(DrmVideoDurationMatchThresholdPercent)}%"; + public string SelectedUsersSummary => $"{AvailableUsers.Count(user => user.IsSelected)} / {AvailableUsers.Count} selected"; @@ -319,6 +343,16 @@ public partial class MainWindowViewModel( FfmpegPathError = string.Empty; } + public void SetFfprobePath(string? path) + { + string normalizedPath = NormalizePathForDisplay(path); + _actualFfprobePath = normalizedPath; + FfprobePath = HidePrivateInfo && !string.IsNullOrWhiteSpace(normalizedPath) + ? "[Hidden for Privacy]" + : normalizedPath; + FfprobePathError = string.Empty; + } + public void SetDownloadPath(string? path) { string normalizedPath = NormalizePathForDisplay(path); @@ -760,6 +794,16 @@ public partial class MainWindowViewModel( FfmpegPathError = string.Empty; } + partial void OnFfprobePathChanged(string value) + { + if (value != "[Hidden for Privacy]") + { + _actualFfprobePath = value; + } + + FfprobePathError = string.Empty; + } + partial void OnDownloadPathChanged(string value) { if (value != "[Hidden for Privacy]") @@ -1009,6 +1053,12 @@ public partial class MainWindowViewModel( continue; } + if (error.Key == nameof(Config.FFprobePath)) + { + FfprobePathError = error.Value; + continue; + } + if (fieldMap.TryGetValue(error.Key, out ConfigFieldViewModel? field)) { field.SetError(error.Value); @@ -1122,12 +1172,18 @@ public partial class MainWindowViewModel( _actualFfmpegPath = ffmpegPath; FfmpegPath = HidePrivateInfo && !string.IsNullOrWhiteSpace(ffmpegPath) ? "[Hidden for Privacy]" : ffmpegPath; + string ffprobePath = NormalizePathForDisplay(config.FFprobePath); + _actualFfprobePath = ffprobePath; + FfprobePath = HidePrivateInfo && !string.IsNullOrWhiteSpace(ffprobePath) ? "[Hidden for Privacy]" : ffprobePath; + string downloadPath = ResolveDownloadPathForDisplay(config.DownloadPath); _actualDownloadPath = downloadPath; DownloadPath = HidePrivateInfo && !string.IsNullOrWhiteSpace(downloadPath) ? "[Hidden for Privacy]" : downloadPath; + DrmVideoDurationMatchThresholdPercent = Math.Clamp(config.DrmVideoDurationMatchThreshold * 100, 0, 100); + ClearSpecialConfigErrors(); PopulateSelectionOptions(MediaTypeOptions, s_mediaTypeOptions, config); @@ -1168,11 +1224,19 @@ public partial class MainWindowViewModel( ? string.Empty : EscapePathForConfig(normalizedFfmpegPath); + string ffprobePathToUse = HidePrivateInfo ? _actualFfprobePath : FfprobePath; + string normalizedFfprobePath = NormalizePathForDisplay(ffprobePathToUse); + config.FFprobePath = string.IsNullOrWhiteSpace(normalizedFfprobePath) + ? string.Empty + : EscapePathForConfig(normalizedFfprobePath); + string downloadPathToUse = HidePrivateInfo ? _actualDownloadPath : DownloadPath; string normalizedDownloadPath = NormalizePathForDisplay(downloadPathToUse); config.DownloadPath = string.IsNullOrWhiteSpace(normalizedDownloadPath) ? EscapePathForConfig(s_defaultDownloadPath) : EscapePathForConfig(normalizedDownloadPath); + config.DrmVideoDurationMatchThreshold = + Math.Clamp(Math.Round(DrmVideoDurationMatchThresholdPercent / 100d, 2), 0d, 1d); ApplySelectionOptionsToConfig(config, MediaTypeOptions); ApplySelectionOptionsToConfig(config, MediaSourceOptions); @@ -1208,13 +1272,14 @@ public partial class MainWindowViewModel( private void ClearSpecialConfigErrors() { FfmpegPathError = string.Empty; + FfprobePathError = string.Empty; DownloadPathError = string.Empty; MediaTypesError = string.Empty; MediaSourcesError = string.Empty; } private bool HasSpecialConfigErrors() => - HasFfmpegPathError || HasDownloadPathError || HasMediaTypesError || HasMediaSourcesError; + HasFfmpegPathError || HasFfprobePathError || HasDownloadPathError || HasMediaTypesError || HasMediaSourcesError; private static string ResolveDownloadPathForDisplay(string? configuredPath) { @@ -1404,7 +1469,9 @@ public partial class MainWindowViewModel( or nameof(Config.NonInteractiveModePurchasedTab) or nameof(Config.DisableBrowserAuth) or nameof(Config.FFmpegPath) + or nameof(Config.FFprobePath) or nameof(Config.DownloadPath) + or nameof(Config.DrmVideoDurationMatchThreshold) or nameof(Config.DownloadVideos) or nameof(Config.DownloadImages) or nameof(Config.DownloadAudios) diff --git a/OF DL.Gui/Views/MainWindow.axaml b/OF DL.Gui/Views/MainWindow.axaml index 230d1ee..59a7d2f 100644 --- a/OF DL.Gui/Views/MainWindow.axaml +++ b/OF DL.Gui/Views/MainWindow.axaml @@ -142,6 +142,42 @@ Foreground="#FF5A5A" Text="{Binding FfmpegPathError}" TextWrapping="Wrap" /> + + + +