Initial commit: Futonizer MKV track stripper
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
<ui:FluentWindow x:Class="Futonizer.Views.HistoryWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="History"
|
||||
Height="520" Width="640"
|
||||
MinHeight="360" MinWidth="480"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowCornerPreference="Round">
|
||||
|
||||
<Window.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
||||
|
||||
<Style x:Key="HistoryListBoxItemStyle" TargetType="ListBoxItem">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
<Setter Property="Padding" Value="10,6"/>
|
||||
<Setter Property="Margin" Value="3,1"/>
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListBoxItem">
|
||||
<Border x:Name="ItemBorder"
|
||||
Background="Transparent"
|
||||
CornerRadius="6"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid>
|
||||
<Border x:Name="AccentBar"
|
||||
Width="3" HorizontalAlignment="Left"
|
||||
Margin="-10,-6,0,-6"
|
||||
CornerRadius="2"
|
||||
Background="{DynamicResource AccentFillColorDefaultBrush}"
|
||||
Visibility="Collapsed"/>
|
||||
<ContentPresenter/>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="ItemBorder" Property="Background"
|
||||
Value="{DynamicResource ControlFillColorSecondaryBrush}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter TargetName="ItemBorder" Property="Background"
|
||||
Value="{DynamicResource AccentFillColorSecondaryBrush}"/>
|
||||
<Setter TargetName="AccentBar" Property="Visibility" Value="Visible"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Title="History"/>
|
||||
|
||||
<Grid Grid.Row="1" Margin="16,4,16,16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" x:Name="SummaryText"
|
||||
FontSize="12" Opacity="0.7" Margin="4,0,0,8"/>
|
||||
|
||||
<Border Grid.Row="1" CornerRadius="8" ClipToBounds="True"
|
||||
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
||||
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
|
||||
BorderThickness="1">
|
||||
<Grid>
|
||||
<TextBlock x:Name="EmptyHint"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Text="No history yet"
|
||||
Opacity="0.4" FontSize="14"
|
||||
IsHitTestVisible="False"/>
|
||||
|
||||
<ListBox x:Name="HistoryList"
|
||||
Background="Transparent" BorderThickness="0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
||||
VirtualizingPanel.ScrollUnit="Pixel"
|
||||
PreviewMouseWheel="HistoryList_PreviewMouseWheel"
|
||||
ItemContainerStyle="{StaticResource HistoryListBoxItemStyle}">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid ToolTip="{Binding Message}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="140"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock Grid.Column="0" Text="✓" FontSize="12"
|
||||
Foreground="#4CAF50" VerticalAlignment="Center"
|
||||
Visibility="{Binding Success, Converter={StaticResource BoolToVis}}"/>
|
||||
<TextBlock Grid.Column="0" Text="✗" FontSize="12"
|
||||
Foreground="#F44336" VerticalAlignment="Center"
|
||||
Visibility="{Binding Failed, Converter={StaticResource BoolToVis}}"/>
|
||||
|
||||
<TextBlock Grid.Column="1" Text="{Binding TimestampDisplay}"
|
||||
FontSize="11" Opacity="0.7" VerticalAlignment="Center" Margin="8,0"/>
|
||||
|
||||
<TextBlock Grid.Column="2" Text="{Binding FileName}"
|
||||
FontSize="12" VerticalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.Windows.Input;
|
||||
using Futonizer.Services;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace Futonizer.Views;
|
||||
|
||||
public partial class HistoryWindow : FluentWindow
|
||||
{
|
||||
public HistoryWindow()
|
||||
{
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
|
||||
InitializeComponent();
|
||||
|
||||
var entries = HistoryService.Load();
|
||||
HistoryList.ItemsSource = entries;
|
||||
EmptyHint.Visibility = entries.Count == 0
|
||||
? System.Windows.Visibility.Visible
|
||||
: System.Windows.Visibility.Collapsed;
|
||||
|
||||
SummaryText.Text = entries.Count == 0
|
||||
? string.Empty
|
||||
: $"{entries.Count} entr{(entries.Count == 1 ? "y" : "ies")} (newest first, capped at 1000)";
|
||||
}
|
||||
|
||||
private void HistoryList_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
=> SmoothScrollHelper.HandlePreviewMouseWheel(sender, e);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<ui:FluentWindow x:Class="Futonizer.Views.ProgressWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="Stripping Tracks"
|
||||
Height="500" Width="720"
|
||||
MinHeight="380" MinWidth="500"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowCornerPreference="Round"
|
||||
Closing="Window_Closing"
|
||||
Loaded="Window_Loaded">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Title="Stripping Tracks"/>
|
||||
|
||||
<Grid Grid.Row="1" Margin="16,8,16,16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Active jobs -->
|
||||
<ItemsControl x:Name="ActiveJobsList" Grid.Row="0" Margin="0,0,0,10">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Margin="0,0,0,6">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ProgressBar Grid.Row="0" Height="6" Minimum="0" Maximum="100" Value="{Binding Percent}"/>
|
||||
<TextBlock Grid.Row="1" Margin="0,2,0,0" FontSize="11" Opacity="0.75" Text="{Binding Display}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Log -->
|
||||
<Border Grid.Row="1" CornerRadius="8"
|
||||
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
||||
BorderBrush="{DynamicResource ControlElevationBorderBrush}" BorderThickness="1">
|
||||
<ui:TextBox x:Name="LogBox" IsReadOnly="True" TextWrapping="NoWrap"
|
||||
AcceptsReturn="True"
|
||||
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
|
||||
FontFamily="Cascadia Mono, Consolas" FontSize="12"
|
||||
BorderThickness="0" Background="Transparent"/>
|
||||
</Border>
|
||||
|
||||
<!-- Status + speed + button -->
|
||||
<Grid Grid.Row="2" Margin="0,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Name="StatusText" Grid.Column="0" VerticalAlignment="Center" FontWeight="SemiBold"/>
|
||||
<TextBlock x:Name="TotalSpeedText" Grid.Column="1" VerticalAlignment="Center"
|
||||
Margin="16,0,16,0" Opacity="0.75"/>
|
||||
<ui:Button x:Name="CancelCloseButton" Grid.Column="2" Content="Cancel" Width="100"
|
||||
Appearance="Secondary" Click="CancelCloseButton_Click"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
@@ -0,0 +1,216 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Futonizer.Models;
|
||||
using Futonizer.Services;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace Futonizer.Views;
|
||||
|
||||
public partial class ProgressWindow : FluentWindow
|
||||
{
|
||||
private const int MaxConcurrentStrips = 5;
|
||||
|
||||
private readonly IReadOnlyList<QueuedFileItem> _files;
|
||||
private readonly string _mkvMergePath;
|
||||
private readonly string _outputFolder;
|
||||
private readonly ObservableCollection<JobProgressItem> _activeJobs = new();
|
||||
|
||||
private CancellationTokenSource? _cts;
|
||||
private bool _isDone;
|
||||
|
||||
/// <summary>
|
||||
/// True once processing has finished running (successfully, with failures,
|
||||
/// or cancelled) — i.e. whenever the Cancel button has turned into Close.
|
||||
/// Used by the caller to decide whether to clear its queue after this
|
||||
/// window is closed.
|
||||
/// </summary>
|
||||
public bool ProcessingCompleted { get; private set; }
|
||||
|
||||
public ProgressWindow(IReadOnlyList<QueuedFileItem> files, string mkvMergePath, string outputFolder)
|
||||
{
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
|
||||
InitializeComponent();
|
||||
|
||||
_files = files;
|
||||
_mkvMergePath = mkvMergePath;
|
||||
_outputFolder = outputFolder;
|
||||
|
||||
Title = $"Stripping {files.Count} file{(files.Count == 1 ? "" : "s")}";
|
||||
ActiveJobsList.ItemsSource = _activeJobs;
|
||||
}
|
||||
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_ = StartProcessingAsync();
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
_cts?.Cancel();
|
||||
}
|
||||
|
||||
private void CancelCloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isDone)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
_cts?.Cancel();
|
||||
CancelCloseButton.IsEnabled = false;
|
||||
StatusText.Text = "Cancelling...";
|
||||
}
|
||||
|
||||
private void AppendLog(string line)
|
||||
{
|
||||
LogBox.AppendText(line + Environment.NewLine);
|
||||
LogBox.ScrollToEnd();
|
||||
}
|
||||
|
||||
private void UpdateTotalSpeed()
|
||||
{
|
||||
if (_activeJobs.Count == 0)
|
||||
{
|
||||
TotalSpeedText.Text = string.Empty;
|
||||
return;
|
||||
}
|
||||
double total = _activeJobs.Sum(j => j.MegabytesPerSecond);
|
||||
TotalSpeedText.Text = _activeJobs.Count == 1
|
||||
? $"{total:F1} MB/s"
|
||||
: $"{total:F1} MB/s across {_activeJobs.Count} file(s)";
|
||||
}
|
||||
|
||||
private async Task StartProcessingAsync()
|
||||
{
|
||||
StatusText.Text = "Stripping tracks...";
|
||||
_cts = new CancellationTokenSource();
|
||||
var ct = _cts.Token;
|
||||
|
||||
bool overwritingInPlace = string.IsNullOrWhiteSpace(_outputFolder);
|
||||
var service = new MkvService(_mkvMergePath);
|
||||
|
||||
AppendLog($"Starting track removal for {_files.Count} file(s) (up to {MaxConcurrentStrips} at a time)...");
|
||||
AppendLog(new string('-', 80));
|
||||
|
||||
int successCount = 0;
|
||||
long totalBytesWritten = 0;
|
||||
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
try
|
||||
{
|
||||
using var semaphore = new SemaphoreSlim(MaxConcurrentStrips);
|
||||
|
||||
var stripTasks = _files.Select(async file =>
|
||||
{
|
||||
await semaphore.WaitAsync(ct);
|
||||
|
||||
var job = new JobProgressItem { FileName = file.FileName };
|
||||
job.PropertyChanged += (_, args) =>
|
||||
{
|
||||
if (args.PropertyName == nameof(JobProgressItem.MegabytesPerSecond))
|
||||
UpdateTotalSpeed();
|
||||
};
|
||||
_activeJobs.Add(job);
|
||||
UpdateTotalSpeed();
|
||||
|
||||
try
|
||||
{
|
||||
string outputFilePath = overwritingInPlace
|
||||
? file.FilePath
|
||||
: Path.Combine(_outputFolder, file.FileName);
|
||||
|
||||
var videoIds = file.Tracks
|
||||
.Where(t => t.Type == "video" && t.Copy)
|
||||
.Select(t => t.Id).ToList();
|
||||
var audioIds = file.Tracks
|
||||
.Where(t => t.Type == "audio" && t.Copy)
|
||||
.Select(t => t.Id).ToList();
|
||||
var subIds = file.Tracks
|
||||
.Where(t => t.Type == "subtitles" && t.Copy)
|
||||
.Select(t => t.Id).ToList();
|
||||
|
||||
bool nothingToStrip = file.Tracks.All(t => t.Copy);
|
||||
|
||||
var progress = new Progress<ProcessingProgress>(p =>
|
||||
{
|
||||
job.Percent = p.PercentComplete;
|
||||
job.MegabytesPerSecond = p.MegabytesPerSecond;
|
||||
});
|
||||
|
||||
var result = nothingToStrip
|
||||
? await service.CopyFileAsync(file.FilePath, outputFilePath, progress, ct)
|
||||
: await service.StripTracksAsync(
|
||||
file.FilePath, outputFilePath,
|
||||
videoIds, audioIds, subIds,
|
||||
progress, ct);
|
||||
|
||||
string tag = result.Success ? "[DONE]" : "[FAIL]";
|
||||
AppendLog($"{tag} {result.FileName} — {result.Message}");
|
||||
|
||||
HistoryService.Add(new HistoryEntry
|
||||
{
|
||||
Timestamp = DateTime.Now,
|
||||
FileName = result.FileName,
|
||||
FilePath = file.FilePath,
|
||||
Success = result.Success,
|
||||
Message = result.Message,
|
||||
});
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
successCount++;
|
||||
try
|
||||
{
|
||||
long bytes = new FileInfo(outputFilePath).Length;
|
||||
Interlocked.Add(ref totalBytesWritten, bytes);
|
||||
}
|
||||
catch { /* best effort */ }
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_activeJobs.Remove(job);
|
||||
UpdateTotalSpeed();
|
||||
semaphore.Release();
|
||||
}
|
||||
}).ToList();
|
||||
|
||||
await Task.WhenAll(stripTasks);
|
||||
|
||||
AppendLog(new string('-', 80));
|
||||
AppendLog($"Processing complete: {successCount}/{_files.Count} file(s) succeeded.");
|
||||
|
||||
stopwatch.Stop();
|
||||
double totalSec = stopwatch.Elapsed.TotalSeconds;
|
||||
double totalMb = totalBytesWritten / 1024.0 / 1024.0;
|
||||
double avgMbps = totalSec > 0 ? totalMb / totalSec : 0;
|
||||
AppendLog($"Total data written: {totalMb / 1024.0:F2} GB in {totalSec:F1}s (avg {avgMbps:F1} MB/s combined).");
|
||||
|
||||
StatusText.Text = $"Done: {successCount}/{_files.Count} succeeded.";
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
AppendLog(new string('-', 80));
|
||||
AppendLog("Cancelled by user. Nothing further was modified.");
|
||||
StatusText.Text = "Cancelled.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AppendLog($"Unexpected error: {ex.Message}");
|
||||
StatusText.Text = "Error.";
|
||||
}
|
||||
finally
|
||||
{
|
||||
_activeJobs.Clear();
|
||||
TotalSpeedText.Text = string.Empty;
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
_isDone = true;
|
||||
ProcessingCompleted = true;
|
||||
CancelCloseButton.IsEnabled = true;
|
||||
CancelCloseButton.Content = "Close";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<ui:FluentWindow x:Class="Futonizer.Views.SettingsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
Title="Settings"
|
||||
Height="320" Width="540"
|
||||
MinHeight="320" MinWidth="540"
|
||||
ResizeMode="NoResize"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
ExtendsContentIntoTitleBar="True"
|
||||
WindowBackdropType="Mica"
|
||||
WindowCornerPreference="Round">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ui:TitleBar Grid.Row="0" Title="Settings"/>
|
||||
|
||||
<Grid Grid.Row="1" Margin="20,8,20,16">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- mkvmerge path -->
|
||||
<TextBlock Grid.Row="0" Text="mkvmerge.exe path" FontSize="12" Opacity="0.7" Margin="0,0,0,4"/>
|
||||
<Grid Grid.Row="1" Margin="0,0,0,14">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBox x:Name="MkvMergePathBox" Grid.Column="0" Margin="0,0,8,0"
|
||||
PlaceholderText="Path to mkvmerge.exe"/>
|
||||
<ui:Button x:Name="BrowseMkvMergeButton" Grid.Column="1" Content="Browse..." Width="100"
|
||||
Click="BrowseMkvMergeButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<!-- Output folder -->
|
||||
<TextBlock Grid.Row="2" Text="Output folder (optional — blank overwrites files in place)" FontSize="12" Opacity="0.7" Margin="0,0,0,4"/>
|
||||
<Grid Grid.Row="3" Margin="0,0,0,14">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ui:TextBox x:Name="OutputFolderBox" Grid.Column="0" Margin="0,0,8,0"
|
||||
PlaceholderText="Leave blank to overwrite original files"/>
|
||||
<ui:Button x:Name="BrowseOutputFolderButton" Grid.Column="1" Content="Browse..." Width="100"
|
||||
Click="BrowseOutputFolderButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<!-- History -->
|
||||
<ui:Button x:Name="ViewHistoryButton" Grid.Row="4" Content="View History..."
|
||||
HorizontalAlignment="Left" Appearance="Secondary"
|
||||
Click="ViewHistoryButton_Click"/>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="6" Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<ui:Button x:Name="CancelButton" Content="Cancel" Width="90" Margin="0,0,10,0"
|
||||
Appearance="Secondary" Click="CancelButton_Click"/>
|
||||
<ui:Button x:Name="SaveButton" Content="Save" Width="90"
|
||||
Appearance="Primary" Click="SaveButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using Microsoft.Win32;
|
||||
using Futonizer.Models;
|
||||
using Wpf.Ui.Controls;
|
||||
|
||||
namespace Futonizer.Views;
|
||||
|
||||
public partial class SettingsWindow : FluentWindow
|
||||
{
|
||||
public AppSettings? Result { get; private set; }
|
||||
|
||||
public SettingsWindow(AppSettings current)
|
||||
{
|
||||
Wpf.Ui.Appearance.SystemThemeWatcher.Watch(this);
|
||||
InitializeComponent();
|
||||
MkvMergePathBox.Text = current.MkvMergePath;
|
||||
OutputFolderBox.Text = current.OutputFolder;
|
||||
}
|
||||
|
||||
private void BrowseMkvMergeButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new OpenFileDialog
|
||||
{
|
||||
Title = "Locate mkvmerge.exe",
|
||||
Filter = "mkvmerge.exe|mkvmerge.exe|Executable files (*.exe)|*.exe|All files (*.*)|*.*",
|
||||
CheckFileExists = true,
|
||||
};
|
||||
if (dlg.ShowDialog(this) == true)
|
||||
MkvMergePathBox.Text = dlg.FileName;
|
||||
}
|
||||
|
||||
private void BrowseOutputFolderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var dlg = new OpenFolderDialog { Title = "Select output folder" };
|
||||
if (dlg.ShowDialog(this) == true)
|
||||
OutputFolderBox.Text = dlg.FolderName;
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Result = new AppSettings
|
||||
{
|
||||
MkvMergePath = MkvMergePathBox.Text.Trim(),
|
||||
OutputFolder = OutputFolderBox.Text.Trim(),
|
||||
};
|
||||
DialogResult = true;
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
}
|
||||
|
||||
private void ViewHistoryButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var win = new HistoryWindow { Owner = this };
|
||||
win.ShowDialog();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user