Add FFprobe Path and DRM Video Duration Match Threshold to the config page
This commit is contained in:
parent
fccee9a520
commit
da40f3d0c5
@ -10,6 +10,7 @@ internal static class ConfigValidationService
|
|||||||
|
|
||||||
ValidatePath(config.DownloadPath, nameof(Config.DownloadPath), errors, requireExistingFile: false);
|
ValidatePath(config.DownloadPath, nameof(Config.DownloadPath), errors, requireExistingFile: false);
|
||||||
ValidatePath(config.FFmpegPath, nameof(Config.FFmpegPath), errors, requireExistingFile: true);
|
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)
|
if (config.Timeout.HasValue && config.Timeout.Value <= 0 && config.Timeout.Value != -1)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -49,8 +49,12 @@ public partial class MainWindowViewModel(
|
|||||||
{
|
{
|
||||||
[nameof(Config.FFmpegPath)] =
|
[nameof(Config.FFmpegPath)] =
|
||||||
"Path to the FFmpeg executable. If blank, OF-DL will try the app directory and PATH.",
|
"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)] =
|
[nameof(Config.DownloadPath)] =
|
||||||
"Base download folder. If blank, OF-DL uses __user_data__/sites/OnlyFans/{username}.",
|
"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.DownloadVideos)] = "Download video media when enabled.",
|
||||||
[nameof(Config.DownloadImages)] = "Download image media when enabled.",
|
[nameof(Config.DownloadImages)] = "Download image media when enabled.",
|
||||||
[nameof(Config.DownloadAudios)] = "Download audio 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;
|
[ObservableProperty] private string _errorMessage = string.Empty;
|
||||||
|
|
||||||
private string _actualFfmpegPath = string.Empty;
|
private string _actualFfmpegPath = string.Empty;
|
||||||
|
private string _actualFfprobePath = string.Empty;
|
||||||
private string _actualDownloadPath = string.Empty;
|
private string _actualDownloadPath = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(FfmpegPathDisplay))]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(FfmpegPathDisplay))]
|
||||||
@ -172,12 +177,21 @@ public partial class MainWindowViewModel(
|
|||||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfmpegPathError))]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasFfmpegPathError))]
|
||||||
private string _ffmpegPathError = string.Empty;
|
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))]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(DownloadPathDisplay))]
|
||||||
private string _downloadPath = string.Empty;
|
private string _downloadPath = string.Empty;
|
||||||
|
|
||||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasDownloadPathError))]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasDownloadPathError))]
|
||||||
private string _downloadPathError = string.Empty;
|
private string _downloadPathError = string.Empty;
|
||||||
|
|
||||||
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(DrmVideoDurationMatchThresholdPercentLabel))]
|
||||||
|
private double _drmVideoDurationMatchThresholdPercent = 98;
|
||||||
|
|
||||||
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasMediaTypesError))]
|
[ObservableProperty] [NotifyPropertyChangedFor(nameof(HasMediaTypesError))]
|
||||||
private string _mediaTypesError = string.Empty;
|
private string _mediaTypesError = string.Empty;
|
||||||
|
|
||||||
@ -218,6 +232,8 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
public bool HasFfmpegPathError => !string.IsNullOrWhiteSpace(FfmpegPathError);
|
public bool HasFfmpegPathError => !string.IsNullOrWhiteSpace(FfmpegPathError);
|
||||||
|
|
||||||
|
public bool HasFfprobePathError => !string.IsNullOrWhiteSpace(FfprobePathError);
|
||||||
|
|
||||||
public bool HasDownloadPathError => !string.IsNullOrWhiteSpace(DownloadPathError);
|
public bool HasDownloadPathError => !string.IsNullOrWhiteSpace(DownloadPathError);
|
||||||
|
|
||||||
public bool HasMediaTypesError => !string.IsNullOrWhiteSpace(MediaTypesError);
|
public bool HasMediaTypesError => !string.IsNullOrWhiteSpace(MediaTypesError);
|
||||||
@ -233,8 +249,16 @@ public partial class MainWindowViewModel(
|
|||||||
|
|
||||||
public string FfmpegPathHelpText => GetConfigHelpText(nameof(Config.FFmpegPath));
|
public string FfmpegPathHelpText => GetConfigHelpText(nameof(Config.FFmpegPath));
|
||||||
|
|
||||||
|
public string FfprobePathHelpText => GetConfigHelpText(nameof(Config.FFprobePath));
|
||||||
|
|
||||||
public string DownloadPathHelpText => GetConfigHelpText(nameof(Config.DownloadPath));
|
public string DownloadPathHelpText => GetConfigHelpText(nameof(Config.DownloadPath));
|
||||||
|
|
||||||
|
public string DrmVideoDurationMatchThresholdHelpText =>
|
||||||
|
GetConfigHelpText(nameof(Config.DrmVideoDurationMatchThreshold));
|
||||||
|
|
||||||
|
public string DrmVideoDurationMatchThresholdPercentLabel =>
|
||||||
|
$"{Math.Round(DrmVideoDurationMatchThresholdPercent)}%";
|
||||||
|
|
||||||
public string SelectedUsersSummary =>
|
public string SelectedUsersSummary =>
|
||||||
$"{AvailableUsers.Count(user => user.IsSelected)} / {AvailableUsers.Count} selected";
|
$"{AvailableUsers.Count(user => user.IsSelected)} / {AvailableUsers.Count} selected";
|
||||||
|
|
||||||
@ -319,6 +343,16 @@ public partial class MainWindowViewModel(
|
|||||||
FfmpegPathError = string.Empty;
|
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)
|
public void SetDownloadPath(string? path)
|
||||||
{
|
{
|
||||||
string normalizedPath = NormalizePathForDisplay(path);
|
string normalizedPath = NormalizePathForDisplay(path);
|
||||||
@ -760,6 +794,16 @@ public partial class MainWindowViewModel(
|
|||||||
FfmpegPathError = string.Empty;
|
FfmpegPathError = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partial void OnFfprobePathChanged(string value)
|
||||||
|
{
|
||||||
|
if (value != "[Hidden for Privacy]")
|
||||||
|
{
|
||||||
|
_actualFfprobePath = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
FfprobePathError = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
partial void OnDownloadPathChanged(string value)
|
partial void OnDownloadPathChanged(string value)
|
||||||
{
|
{
|
||||||
if (value != "[Hidden for Privacy]")
|
if (value != "[Hidden for Privacy]")
|
||||||
@ -1009,6 +1053,12 @@ public partial class MainWindowViewModel(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error.Key == nameof(Config.FFprobePath))
|
||||||
|
{
|
||||||
|
FfprobePathError = error.Value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldMap.TryGetValue(error.Key, out ConfigFieldViewModel? field))
|
if (fieldMap.TryGetValue(error.Key, out ConfigFieldViewModel? field))
|
||||||
{
|
{
|
||||||
field.SetError(error.Value);
|
field.SetError(error.Value);
|
||||||
@ -1122,12 +1172,18 @@ public partial class MainWindowViewModel(
|
|||||||
_actualFfmpegPath = ffmpegPath;
|
_actualFfmpegPath = ffmpegPath;
|
||||||
FfmpegPath = HidePrivateInfo && !string.IsNullOrWhiteSpace(ffmpegPath) ? "[Hidden for Privacy]" : 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);
|
string downloadPath = ResolveDownloadPathForDisplay(config.DownloadPath);
|
||||||
_actualDownloadPath = downloadPath;
|
_actualDownloadPath = downloadPath;
|
||||||
DownloadPath = HidePrivateInfo && !string.IsNullOrWhiteSpace(downloadPath)
|
DownloadPath = HidePrivateInfo && !string.IsNullOrWhiteSpace(downloadPath)
|
||||||
? "[Hidden for Privacy]"
|
? "[Hidden for Privacy]"
|
||||||
: downloadPath;
|
: downloadPath;
|
||||||
|
|
||||||
|
DrmVideoDurationMatchThresholdPercent = Math.Clamp(config.DrmVideoDurationMatchThreshold * 100, 0, 100);
|
||||||
|
|
||||||
ClearSpecialConfigErrors();
|
ClearSpecialConfigErrors();
|
||||||
|
|
||||||
PopulateSelectionOptions(MediaTypeOptions, s_mediaTypeOptions, config);
|
PopulateSelectionOptions(MediaTypeOptions, s_mediaTypeOptions, config);
|
||||||
@ -1168,11 +1224,19 @@ public partial class MainWindowViewModel(
|
|||||||
? string.Empty
|
? string.Empty
|
||||||
: EscapePathForConfig(normalizedFfmpegPath);
|
: 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 downloadPathToUse = HidePrivateInfo ? _actualDownloadPath : DownloadPath;
|
||||||
string normalizedDownloadPath = NormalizePathForDisplay(downloadPathToUse);
|
string normalizedDownloadPath = NormalizePathForDisplay(downloadPathToUse);
|
||||||
config.DownloadPath = string.IsNullOrWhiteSpace(normalizedDownloadPath)
|
config.DownloadPath = string.IsNullOrWhiteSpace(normalizedDownloadPath)
|
||||||
? EscapePathForConfig(s_defaultDownloadPath)
|
? EscapePathForConfig(s_defaultDownloadPath)
|
||||||
: EscapePathForConfig(normalizedDownloadPath);
|
: EscapePathForConfig(normalizedDownloadPath);
|
||||||
|
config.DrmVideoDurationMatchThreshold =
|
||||||
|
Math.Clamp(Math.Round(DrmVideoDurationMatchThresholdPercent / 100d, 2), 0d, 1d);
|
||||||
|
|
||||||
ApplySelectionOptionsToConfig(config, MediaTypeOptions);
|
ApplySelectionOptionsToConfig(config, MediaTypeOptions);
|
||||||
ApplySelectionOptionsToConfig(config, MediaSourceOptions);
|
ApplySelectionOptionsToConfig(config, MediaSourceOptions);
|
||||||
@ -1208,13 +1272,14 @@ public partial class MainWindowViewModel(
|
|||||||
private void ClearSpecialConfigErrors()
|
private void ClearSpecialConfigErrors()
|
||||||
{
|
{
|
||||||
FfmpegPathError = string.Empty;
|
FfmpegPathError = string.Empty;
|
||||||
|
FfprobePathError = string.Empty;
|
||||||
DownloadPathError = string.Empty;
|
DownloadPathError = string.Empty;
|
||||||
MediaTypesError = string.Empty;
|
MediaTypesError = string.Empty;
|
||||||
MediaSourcesError = string.Empty;
|
MediaSourcesError = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasSpecialConfigErrors() =>
|
private bool HasSpecialConfigErrors() =>
|
||||||
HasFfmpegPathError || HasDownloadPathError || HasMediaTypesError || HasMediaSourcesError;
|
HasFfmpegPathError || HasFfprobePathError || HasDownloadPathError || HasMediaTypesError || HasMediaSourcesError;
|
||||||
|
|
||||||
private static string ResolveDownloadPathForDisplay(string? configuredPath)
|
private static string ResolveDownloadPathForDisplay(string? configuredPath)
|
||||||
{
|
{
|
||||||
@ -1404,7 +1469,9 @@ public partial class MainWindowViewModel(
|
|||||||
or nameof(Config.NonInteractiveModePurchasedTab)
|
or nameof(Config.NonInteractiveModePurchasedTab)
|
||||||
or nameof(Config.DisableBrowserAuth)
|
or nameof(Config.DisableBrowserAuth)
|
||||||
or nameof(Config.FFmpegPath)
|
or nameof(Config.FFmpegPath)
|
||||||
|
or nameof(Config.FFprobePath)
|
||||||
or nameof(Config.DownloadPath)
|
or nameof(Config.DownloadPath)
|
||||||
|
or nameof(Config.DrmVideoDurationMatchThreshold)
|
||||||
or nameof(Config.DownloadVideos)
|
or nameof(Config.DownloadVideos)
|
||||||
or nameof(Config.DownloadImages)
|
or nameof(Config.DownloadImages)
|
||||||
or nameof(Config.DownloadAudios)
|
or nameof(Config.DownloadAudios)
|
||||||
|
|||||||
@ -142,6 +142,42 @@
|
|||||||
Foreground="#FF5A5A"
|
Foreground="#FF5A5A"
|
||||||
Text="{Binding FfmpegPathError}"
|
Text="{Binding FfmpegPathError}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="6" Margin="0,8,0,0">
|
||||||
|
<TextBlock FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="FFprobe Path" />
|
||||||
|
<Button Width="20"
|
||||||
|
Height="20"
|
||||||
|
MinWidth="20"
|
||||||
|
MinHeight="20"
|
||||||
|
Padding="0"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Background="#EAF0FB"
|
||||||
|
BorderBrush="#C5D4EC"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="10"
|
||||||
|
Content="?"
|
||||||
|
ToolTip.Tip="{Binding FfprobePathHelpText}" />
|
||||||
|
</StackPanel>
|
||||||
|
<Grid ColumnDefinitions="*,Auto">
|
||||||
|
<TextBox Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{Binding FfprobePath}"
|
||||||
|
Watermark="Select ffprobe executable" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Margin="8,0,0,0"
|
||||||
|
Classes="secondary"
|
||||||
|
Content="Browse..."
|
||||||
|
Click="OnBrowseFfprobePathClick" />
|
||||||
|
</Grid>
|
||||||
|
<TextBlock IsVisible="{Binding HasFfprobePathError}"
|
||||||
|
Foreground="#FF5A5A"
|
||||||
|
Text="{Binding FfprobePathError}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
@ -291,6 +327,51 @@
|
|||||||
Foreground="#FF5A5A"
|
Foreground="#FF5A5A"
|
||||||
Text="{Binding ViewModel.DownloadPathError, RelativeSource={RelativeSource AncestorType=views:MainWindow}, FallbackValue=''}"
|
Text="{Binding ViewModel.DownloadPathError, RelativeSource={RelativeSource AncestorType=views:MainWindow}, FallbackValue=''}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<Grid Margin="0,8,0,2" ColumnDefinitions="190,*">
|
||||||
|
<Grid Grid.Column="0"
|
||||||
|
ColumnDefinitions="*,Auto"
|
||||||
|
Margin="0,6,10,0"
|
||||||
|
ClipToBounds="True">
|
||||||
|
<TextBlock Grid.Column="0"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#1F2A44"
|
||||||
|
Text="DRM Video Duration Match Threshold"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
VerticalAlignment="Top" />
|
||||||
|
<Button Grid.Column="1"
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
MinWidth="20"
|
||||||
|
MinHeight="20"
|
||||||
|
Margin="6,0,0,0"
|
||||||
|
Padding="0"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
HorizontalContentAlignment="Center"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
FontSize="12"
|
||||||
|
FontWeight="Bold"
|
||||||
|
Background="#EAF0FB"
|
||||||
|
BorderBrush="#C5D4EC"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="10"
|
||||||
|
Content="?"
|
||||||
|
ToolTip.Tip="{Binding ViewModel.DrmVideoDurationMatchThresholdHelpText, RelativeSource={RelativeSource AncestorType=views:MainWindow}, FallbackValue=''}" />
|
||||||
|
</Grid>
|
||||||
|
<Grid Grid.Column="1" ColumnDefinitions="*,Auto" VerticalAlignment="Center">
|
||||||
|
<Slider Grid.Column="0"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="100"
|
||||||
|
Value="{Binding ViewModel.DrmVideoDurationMatchThresholdPercent, RelativeSource={RelativeSource AncestorType=views:MainWindow}, FallbackValue=98}" />
|
||||||
|
<TextBlock Grid.Column="1"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontWeight="SemiBold"
|
||||||
|
Foreground="#4A5B78"
|
||||||
|
Text="{Binding ViewModel.DrmVideoDurationMatchThresholdPercentLabel, RelativeSource={RelativeSource AncestorType=views:MainWindow}, FallbackValue='98%'}" />
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel IsVisible="{Binding HasSpecificDateFilterFields}"
|
<StackPanel IsVisible="{Binding HasSpecificDateFilterFields}"
|
||||||
Margin="0,0,0,6"
|
Margin="0,0,0,6"
|
||||||
|
|||||||
@ -70,6 +70,42 @@ public partial class MainWindow : Window
|
|||||||
vm.SetFfmpegPath(selectedFile.Name);
|
vm.SetFfmpegPath(selectedFile.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void OnBrowseFfprobePathClick(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
if (DataContext is not MainWindowViewModel vm)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TopLevel? topLevel = TopLevel.GetTopLevel(this);
|
||||||
|
if (topLevel?.StorageProvider == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IReadOnlyList<IStorageFile> selectedFiles = await topLevel.StorageProvider.OpenFilePickerAsync(
|
||||||
|
new FilePickerOpenOptions
|
||||||
|
{
|
||||||
|
Title = "Select FFprobe executable",
|
||||||
|
AllowMultiple = false
|
||||||
|
});
|
||||||
|
|
||||||
|
IStorageFile? selectedFile = selectedFiles.FirstOrDefault();
|
||||||
|
if (selectedFile == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string? localPath = selectedFile.TryGetLocalPath();
|
||||||
|
if (!string.IsNullOrWhiteSpace(localPath))
|
||||||
|
{
|
||||||
|
vm.SetFfprobePath(localPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.SetFfprobePath(selectedFile.Name);
|
||||||
|
}
|
||||||
|
|
||||||
private async void OnBrowseDownloadPathClick(object? sender, RoutedEventArgs e)
|
private async void OnBrowseDownloadPathClick(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (DataContext is not MainWindowViewModel vm)
|
if (DataContext is not MainWindowViewModel vm)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user