Drag in subtitle files alongside .mkv files and they're matched to the right video by episode code (S04E06, zero-padding and separator insensitive, plus 4x06 as a fallback) and muxed in as extra subtitle tracks. Any subtitle format mkvmerge accepts is recognized: SRT, (S)SA, VobSub, WebVTT, PGS/SUP, USF, SAMI, TTML/DFXP, STL, Kate. Matching works regardless of drop order — subtitles dropped before their video are held pending until a matching video shows up, and vice versa. Language, forced, and SDH flags are guessed from common filename tokens (e.g. "eng", "forced", "sdh") to set the muxed track's language/name/flags; unmatched subtitles are reported in the status bar.
422 lines
27 KiB
XML
422 lines
27 KiB
XML
<ui:FluentWindow x:Class="Futonizer.MainWindow"
|
|
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="Futonizer"
|
|
Icon="pack://application:,,,/Assets/app.png"
|
|
Height="900" Width="1600" MinHeight="650" MinWidth="1100"
|
|
ExtendsContentIntoTitleBar="True"
|
|
WindowBackdropType="Mica"
|
|
WindowCornerPreference="Round"
|
|
WindowStartupLocation="CenterScreen"
|
|
AllowDrop="True"
|
|
DragOver="Window_DragOver"
|
|
Drop="Window_Drop"
|
|
PreviewKeyDown="Window_PreviewKeyDown">
|
|
|
|
<Window.Resources>
|
|
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
|
|
|
<Style x:Key="QueueListBoxItemStyle" TargetType="ListBoxItem">
|
|
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
|
<Setter Property="Padding" Value="8,5"/>
|
|
<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="-8,-5,0,-5"
|
|
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="Futonizer">
|
|
<ui:TitleBar.Icon>
|
|
<ui:ImageIcon Source="pack://application:,,,/Assets/app.png"/>
|
|
</ui:TitleBar.Icon>
|
|
</ui:TitleBar>
|
|
|
|
<Grid Grid.Row="1" Margin="16,4,16,16">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- ── Toolbar ───────────────────────────────────────────────── -->
|
|
<Grid Grid.Row="0" Margin="0,0,0,10">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<ui:Button Grid.Column="0" Content="Settings" Icon="{ui:SymbolIcon Settings24}"
|
|
Appearance="Secondary"
|
|
Click="SettingsButton_Click"/>
|
|
|
|
<TextBlock x:Name="StatusText" Grid.Column="2" VerticalAlignment="Center"
|
|
Margin="16,0" FontWeight="SemiBold" FontSize="12"/>
|
|
|
|
<ui:Button x:Name="RunButton" Grid.Column="3"
|
|
Content="Strip Tracks" Appearance="Primary"
|
|
Icon="{ui:SymbolIcon Delete24}"
|
|
Click="RunButton_Click"/>
|
|
</Grid>
|
|
|
|
<!-- ── Two-pane content ──────────────────────────────────────── -->
|
|
<Grid Grid.Row="1">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*" MinWidth="280"/>
|
|
<ColumnDefinition Width="8"/>
|
|
<ColumnDefinition Width="*" MinWidth="300"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- File queue panel -->
|
|
<Border Grid.Column="0" CornerRadius="8" ClipToBounds="True"
|
|
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
|
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
|
|
BorderThickness="1">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Queue header -->
|
|
<Grid Grid.Row="0" Margin="10,8,6,4">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
<TextBlock Grid.Column="0" Text="Queue"
|
|
FontWeight="SemiBold" FontSize="13"
|
|
VerticalAlignment="Center"/>
|
|
<Button x:Name="ClearQueueButton" Grid.Column="1"
|
|
Content="Clear" Click="ClearQueueButton_Click"
|
|
ToolTip="Remove all files from the queue"
|
|
Background="Transparent" BorderThickness="0"
|
|
Padding="6,2" Cursor="Hand" FontSize="11"
|
|
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
|
|
IsEnabled="False"/>
|
|
</Grid>
|
|
|
|
<Grid Grid.Row="1">
|
|
<TextBlock x:Name="DropHint"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Text="Drop .mkv files (and subtitles) onto this window"
|
|
Opacity="0.4" FontSize="14" TextAlignment="Center"
|
|
IsHitTestVisible="False"/>
|
|
|
|
<ListBox x:Name="FileQueueList"
|
|
Background="Transparent" BorderThickness="0"
|
|
HorizontalContentAlignment="Stretch"
|
|
SelectionMode="Extended"
|
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
|
|
VirtualizingPanel.ScrollUnit="Pixel"
|
|
PreviewMouseWheel="SmoothList_PreviewMouseWheel"
|
|
PreviewKeyDown="FileQueueList_PreviewKeyDown"
|
|
ItemContainerStyle="{StaticResource QueueListBoxItemStyle}"
|
|
SelectionChanged="FileQueueList_SelectionChanged">
|
|
<ListBox.ItemTemplate>
|
|
<DataTemplate>
|
|
<Grid ToolTip="{Binding Status}">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="16"/>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="18"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Status glyphs (only one visible at a time) -->
|
|
<TextBlock Grid.Column="0" Text="⟳" FontSize="12"
|
|
VerticalAlignment="Center"
|
|
Visibility="{Binding IsLoading, Converter={StaticResource BoolToVis}}"/>
|
|
<TextBlock Grid.Column="0" Text="●" FontSize="9"
|
|
Opacity="0.5" VerticalAlignment="Center"
|
|
Visibility="{Binding IsLoadedNeutral, Converter={StaticResource BoolToVis}}"/>
|
|
<TextBlock Grid.Column="0" Text="✓" FontSize="12"
|
|
Foreground="#4CAF50" VerticalAlignment="Center"
|
|
Visibility="{Binding IsCorrect, Converter={StaticResource BoolToVis}}"/>
|
|
<TextBlock Grid.Column="0" Text="✗" FontSize="12"
|
|
Foreground="#F44336" VerticalAlignment="Center"
|
|
Visibility="{Binding IsIncorrect, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<TextBlock Grid.Column="1" Text="{Binding DisplayFileName}"
|
|
FontSize="12" VerticalAlignment="Center" Margin="6,0"
|
|
TextTrimming="CharacterEllipsis"/>
|
|
|
|
<Border Grid.Column="2" CornerRadius="8" Padding="5,1" Margin="2,0"
|
|
VerticalAlignment="Center"
|
|
Background="{DynamicResource AccentFillColorSecondaryBrush}"
|
|
Visibility="{Binding HasExternalSubtitles, Converter={StaticResource BoolToVis}}"
|
|
ToolTip="{Binding ExternalSubtitlesTooltip}">
|
|
<TextBlock Text="{Binding ExternalSubtitles.Count, StringFormat={}+{0} sub}"
|
|
FontSize="9" FontWeight="SemiBold"/>
|
|
</Border>
|
|
|
|
<TextBlock Grid.Column="3" Text="{Binding FileSizeDisplay}"
|
|
FontSize="11" VerticalAlignment="Center" Margin="4,0"
|
|
Opacity="0.6"
|
|
Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
|
|
|
|
<Button Grid.Column="4" Content="×"
|
|
Tag="{Binding}"
|
|
Click="RemoveFile_Click"
|
|
Background="Transparent" BorderThickness="0"
|
|
Padding="2,0" Cursor="Hand" FontSize="13"
|
|
VerticalAlignment="Center"
|
|
Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
|
|
</Grid>
|
|
</DataTemplate>
|
|
</ListBox.ItemTemplate>
|
|
</ListBox>
|
|
</Grid>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- GridSplitter -->
|
|
<GridSplitter Grid.Column="1" Width="6"
|
|
HorizontalAlignment="Center" Background="Transparent"/>
|
|
|
|
<!-- Track table panel -->
|
|
<Border Grid.Column="2" CornerRadius="8" ClipToBounds="True"
|
|
Background="{DynamicResource ControlFillColorDefaultBrush}"
|
|
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
|
|
BorderThickness="1">
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/>
|
|
<RowDefinition Height="*"/>
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- External subtitles matched to the selected file -->
|
|
<Border x:Name="ExternalSubsPanel" Grid.Row="0" Margin="10,8,10,0"
|
|
Padding="8,6" CornerRadius="6" Visibility="Collapsed"
|
|
Background="{DynamicResource ControlFillColorSecondaryBrush}">
|
|
<StackPanel>
|
|
<TextBlock Text="External subtitles to mux in" FontSize="11"
|
|
FontWeight="SemiBold" Opacity="0.7" Margin="0,0,0,4"/>
|
|
<ItemsControl x:Name="ExternalSubsList">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<WrapPanel Orientation="Horizontal"/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
<ItemsControl.ItemTemplate>
|
|
<DataTemplate>
|
|
<Border Background="{DynamicResource ControlFillColorDefaultBrush}"
|
|
BorderBrush="{DynamicResource ControlElevationBorderBrush}"
|
|
BorderThickness="1" CornerRadius="4"
|
|
Margin="0,0,6,6" Padding="6,3">
|
|
<StackPanel Orientation="Horizontal">
|
|
<TextBlock Text="{Binding LanguageDisplay}" FontSize="11"
|
|
FontWeight="SemiBold" VerticalAlignment="Center"/>
|
|
<TextBlock Text="{Binding FileName}" FontSize="11" Opacity="0.6"
|
|
Margin="6,0,0,0" VerticalAlignment="Center"
|
|
MaxWidth="220" TextTrimming="CharacterEllipsis"/>
|
|
<TextBlock Text="DEFAULT" FontSize="9" FontWeight="Bold"
|
|
Foreground="{DynamicResource AccentTextFillColorPrimaryBrush}"
|
|
Margin="6,0,0,0" VerticalAlignment="Center"
|
|
Visibility="{Binding IsDefault, Converter={StaticResource BoolToVis}}"/>
|
|
<Button Content="×" Tag="{Binding}"
|
|
Click="RemoveExternalSubtitle_Click"
|
|
Background="Transparent" BorderThickness="0"
|
|
Padding="6,0,0,0" Cursor="Hand" FontSize="13"
|
|
VerticalAlignment="Center"
|
|
Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</DataTemplate>
|
|
</ItemsControl.ItemTemplate>
|
|
</ItemsControl>
|
|
</StackPanel>
|
|
</Border>
|
|
|
|
<Grid Grid.Row="1">
|
|
<TextBlock x:Name="NoSelectionHint"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Text="Select a file to view its tracks"
|
|
Opacity="0.4" FontSize="14"
|
|
IsHitTestVisible="False"/>
|
|
|
|
<DataGrid x:Name="TrackGrid"
|
|
Visibility="Collapsed"
|
|
AutoGenerateColumns="False"
|
|
CanUserAddRows="False"
|
|
CanUserDeleteRows="False"
|
|
CanUserReorderColumns="False"
|
|
IsReadOnly="False"
|
|
SelectionMode="Single"
|
|
HeadersVisibility="Column"
|
|
Background="Transparent"
|
|
BorderThickness="0"
|
|
RowHeight="30"
|
|
GridLinesVisibility="None"
|
|
VirtualizingPanel.ScrollUnit="Pixel"
|
|
PreviewMouseWheel="SmoothList_PreviewMouseWheel"
|
|
PreviewKeyDown="TrackGrid_PreviewKeyDown"
|
|
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
|
ScrollViewer.VerticalScrollBarVisibility="Auto">
|
|
<DataGrid.RowStyle>
|
|
<Style TargetType="DataGridRow">
|
|
<Setter Property="Background" Value="Transparent"/>
|
|
<Setter Property="BorderBrush" Value="{DynamicResource ControlElevationBorderBrush}"/>
|
|
<Setter Property="BorderThickness" Value="0"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsGroupStart}" Value="True">
|
|
<Setter Property="BorderThickness" Value="0,1,0,0"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding Type}" Value="video">
|
|
<Setter Property="Opacity" Value="0.5"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding Type}" Value="chapters">
|
|
<Setter Property="Opacity" Value="0.5"/>
|
|
</DataTrigger>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding Type}" Value="audio"/>
|
|
<Condition Binding="{Binding Copy}" Value="False"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Opacity" Value="0.5"/>
|
|
</MultiDataTrigger>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding Type}" Value="subtitles"/>
|
|
<Condition Binding="{Binding Copy}" Value="False"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Opacity" Value="0.5"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</DataGrid.RowStyle>
|
|
<DataGrid.CellStyle>
|
|
<Style TargetType="DataGridCell">
|
|
<Setter Property="Background" Value="Transparent"/>
|
|
<Setter Property="BorderBrush" Value="Transparent"/>
|
|
<Setter Property="BorderThickness" Value="0"/>
|
|
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}"/>
|
|
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
|
<Style.Triggers>
|
|
<Trigger Property="IsSelected" Value="True">
|
|
<Setter Property="Background" Value="Transparent"/>
|
|
<Setter Property="BorderBrush" Value="Transparent"/>
|
|
<Setter Property="Foreground" Value="{DynamicResource TextFillColorPrimaryBrush}"/>
|
|
</Trigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</DataGrid.CellStyle>
|
|
<DataGrid.Columns>
|
|
<!-- Copy (editable checkbox) -->
|
|
<DataGridTemplateColumn Header="Copy" Width="55"
|
|
CanUserSort="False" CanUserResize="False">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<CheckBox IsChecked="{Binding Copy, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
|
IsEnabled="{Binding IsEditable}"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
|
|
<DataGridTextColumn Header="ID"
|
|
Binding="{Binding IdDisplay}" Width="45" IsReadOnly="True"/>
|
|
<DataGridTextColumn Header="Language"
|
|
Binding="{Binding Language}" Width="100" IsReadOnly="True"/>
|
|
<DataGridTextColumn Header="Name"
|
|
Binding="{Binding DisplayName}" Width="*" IsReadOnly="True"/>
|
|
<DataGridTextColumn Header="Codec"
|
|
Binding="{Binding Codec}" Width="185" IsReadOnly="True"/>
|
|
|
|
<!-- Default (read-only indicator) -->
|
|
<DataGridTemplateColumn Header="Default" Width="70"
|
|
CanUserSort="False" CanUserResize="False">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<CheckBox IsChecked="{Binding DefaultTrack, Mode=OneWay}"
|
|
IsHitTestVisible="False" IsTabStop="False"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
|
|
<!-- Forced (read-only indicator) -->
|
|
<DataGridTemplateColumn Header="Forced" Width="70"
|
|
CanUserSort="False" CanUserResize="False">
|
|
<DataGridTemplateColumn.CellTemplate>
|
|
<DataTemplate>
|
|
<CheckBox IsChecked="{Binding ForcedDisplay, Mode=OneWay}"
|
|
IsHitTestVisible="False" IsTabStop="False"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
</DataTemplate>
|
|
</DataGridTemplateColumn.CellTemplate>
|
|
</DataGridTemplateColumn>
|
|
</DataGrid.Columns>
|
|
</DataGrid>
|
|
</Grid>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- Processing overlay: dims the entire content area while stripping is running -->
|
|
<Border x:Name="ProcessingOverlay"
|
|
Grid.ColumnSpan="3"
|
|
Background="#99000000"
|
|
CornerRadius="8"
|
|
Visibility="Collapsed"
|
|
IsHitTestVisible="True"
|
|
Panel.ZIndex="10"/>
|
|
|
|
<!-- Loading overlay: blocks interaction until every queued file has finished loading -->
|
|
<Border x:Name="LoadingOverlay"
|
|
Grid.ColumnSpan="3"
|
|
Background="#B3000000"
|
|
CornerRadius="8"
|
|
Visibility="Collapsed"
|
|
IsHitTestVisible="True">
|
|
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Width="240">
|
|
<TextBlock x:Name="LoadingOverlayText" Text="Loading files..."
|
|
FontSize="14" FontWeight="Medium" Foreground="White"
|
|
HorizontalAlignment="Center" Margin="0,0,0,10"/>
|
|
<ProgressBar x:Name="LoadingProgressBar"
|
|
Height="4" Minimum="0" Maximum="1" Value="0"/>
|
|
</StackPanel>
|
|
</Border>
|
|
</Grid>
|
|
</Grid>
|
|
</Grid>
|
|
</ui:FluentWindow>
|