Miglioramenti UI e funzionalità di configurazione
- Tradotta l'interfaccia utente in italiano. - Migliorato lo stile visivo con nuovi colori e stili. - Riorganizzata la struttura dell'interfaccia per maggiore chiarezza. - Aggiunte risorse di colore e stili riutilizzabili. - Introdotta barra di stato con riepilogo dei job. - Ottimizzato il codice per leggibilità e manutenzione. - Aggiunte nuove opzioni di configurazione (naming, output, frame). - Migliorata la gestione degli errori e dei messaggi di stato. - Aumentato il limite di caricamento delle anteprime a 60. - Rimosso codice obsoleto e duplicato. - Migliorata la compatibilità con WPF (binding e trigger). - Riorganizzate e tradotte le finestre di dialogo. - Migliorate le finestre di configurazione dei job e delle impostazioni. - Semplificata la gestione di FFmpeg e rimosso codice legacy.
This commit is contained in:
@@ -5,127 +5,245 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Ganimede"
|
||||
mc:Ignorable="d"
|
||||
Title="Ganimede - Frame Extractor" Height="800" Width="1200"
|
||||
Background="#222">
|
||||
Title="Estrattore Frame Video" Height="800" Width="1250"
|
||||
Background="#1E2228" WindowStartupLocation="CenterScreen">
|
||||
<Window.Resources>
|
||||
<local:StatusColorConverter x:Key="StatusColorConverter"/>
|
||||
|
||||
<!-- Color resources -->
|
||||
<Color x:Key="AccentColor">#268BFF</Color>
|
||||
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource AccentColor}"/>
|
||||
<SolidColorBrush x:Key="AccentBrushLight" Color="#39A3FF"/>
|
||||
<SolidColorBrush x:Key="BaseBrush" Color="#1E2228"/>
|
||||
<SolidColorBrush x:Key="PanelBrush" Color="#242A31"/>
|
||||
<SolidColorBrush x:Key="PanelSubBrush" Color="#2C333B"/>
|
||||
<SolidColorBrush x:Key="BorderBrushColor" Color="#38424D"/>
|
||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#FFFFFF"/>
|
||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#B5BDC7"/>
|
||||
|
||||
<!-- Button Style -->
|
||||
<Style TargetType="Button" x:Key="ToolbarButton">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Background" Value="#2F3740"/>
|
||||
<Setter Property="BorderBrush" Value="#3F4A55"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="14 8"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="MinHeight" Value="40"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="SnapsToDevicePixels" Value="True"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="8" SnapsToDevicePixels="True">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#39444F"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#46525E"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.4"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Accent Button -->
|
||||
<Style TargetType="Button" x:Key="AccentButton" BasedOn="{StaticResource ToolbarButton}">
|
||||
<Setter Property="Background" Value="{StaticResource AccentBrush}"/>
|
||||
<Setter Property="BorderBrush" Value="#1673D5"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="{StaticResource AccentBrushLight}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Danger Button -->
|
||||
<Style TargetType="Button" x:Key="DangerButton" BasedOn="{StaticResource ToolbarButton}">
|
||||
<Setter Property="Background" Value="#D9534F"/>
|
||||
<Setter Property="BorderBrush" Value="#B33E3B"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#E36460"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- Flat small badge button -->
|
||||
<Style TargetType="Button" x:Key="SmallGhostButton" BasedOn="{StaticResource ToolbarButton}">
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="Padding" Value="12 6"/>
|
||||
<Setter Property="MinHeight" Value="36"/>
|
||||
<Setter Property="Background" Value="#2F3740"/>
|
||||
</Style>
|
||||
|
||||
<!-- ProgressBar -->
|
||||
<Style TargetType="ProgressBar">
|
||||
<Setter Property="Height" Value="6"/>
|
||||
<Setter Property="Foreground" Value="{StaticResource AccentBrush}"/>
|
||||
<Setter Property="Background" Value="#313941"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ProgressBar">
|
||||
<Border Background="{TemplateBinding Background}" CornerRadius="3">
|
||||
<Grid x:Name="PART_Track">
|
||||
<Rectangle x:Name="PART_Indicator" Fill="{TemplateBinding Foreground}" RadiusX="3" RadiusY="3"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- ScrollViewer styling for thin scrollbar -->
|
||||
<Style TargetType="ScrollBar">
|
||||
<Setter Property="Width" Value="10"/>
|
||||
<Setter Property="Background" Value="#20252B"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="10">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Top controls condensed -->
|
||||
<DockPanel Grid.Row="0" LastChildFill="False" Margin="0,0,0,10">
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left" Margin="0,0,15,0">
|
||||
<Button x:Name="BrowseVideoButton" Content="Aggiungi Video" Width="120" Height="35" Margin="0,0,8,0" Click="BrowseVideoButton_Click"/>
|
||||
<Button x:Name="ImportFolderButton" Content="Importa Cartella" Width="130" Height="35" Margin="0,0,8,0" Click="ImportFolderButton_Click"/>
|
||||
<Button x:Name="SelectOutputFolderButton" Content="Cartella Output" Width="130" Height="35" Margin="0,0,8,0" Click="SelectOutputFolderButton_Click"/>
|
||||
<Button x:Name="SettingsButton" Content="Impostazioni" Width="110" Height="35" Margin="0,0,8,0" Click="SettingsButton_Click"/>
|
||||
<!-- TOOLBAR -->
|
||||
<Border Background="#242A31" Padding="16 12" BorderBrush="#303840" BorderThickness="0,0,0,1">
|
||||
<DockPanel LastChildFill="False">
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left">
|
||||
<Button x:Name="BrowseVideoButton" Style="{StaticResource AccentButton}" Content="➕ Aggiungi Video" Click="BrowseVideoButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="ImportFolderButton" Style="{StaticResource ToolbarButton}" Content="📁 Importa Cartella" Click="ImportFolderButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="SelectOutputFolderButton" Style="{StaticResource ToolbarButton}" Content="🗂 Seleziona Cartella Output" Click="SelectOutputFolderButton_Click" Margin="0,0,10,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
|
||||
<Button x:Name="ConfigureSelectedButton" Style="{StaticResource ToolbarButton}" Content="⚙ Configura Selezionati" Width="195" IsEnabled="False" Click="ConfigureSelectedButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="StartQueueButton" Style="{StaticResource AccentButton}" Content="▶ Avvia Coda" Width="150" Click="StartQueueButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="StopQueueButton" Style="{StaticResource DangerButton}" Content="⏹ Ferma" Width="110" IsEnabled="False" Click="StopQueueButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="ClearCompletedButton" Style="{StaticResource SmallGhostButton}" Content="🧹 Pulisci Completati" Click="ClearCompletedButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="ClearAllButton" Style="{StaticResource SmallGhostButton}" Content="🗑 Pulisci Tutto" Click="ClearAllButton_Click" Margin="0,0,10,0"/>
|
||||
<Button x:Name="SettingsButton" Style="{StaticResource SmallGhostButton}" Content="⚙ Impostazioni" Click="SettingsButton_Click"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<!-- CONTENUTO PRINCIPALE -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="370"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Coda -->
|
||||
<Grid Margin="18 12 8 12">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Margin="0 0 0 10" VerticalAlignment="Center">
|
||||
<TextBlock Text="Coda Job" FontSize="18" FontWeight="SemiBold" Foreground="{StaticResource TextPrimaryBrush}"/>
|
||||
<TextBlock x:Name="QueueCountText" Text="(0)" Foreground="{StaticResource TextSecondaryBrush}" Margin="8,4,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border Grid.Row="1" Background="{StaticResource PanelBrush}" BorderBrush="{StaticResource BorderBrushColor}" BorderThickness="1" CornerRadius="8" Padding="4">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="QueueItemsControl">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="{StaticResource PanelSubBrush}" Margin="6" Padding="10" CornerRadius="6" BorderBrush="#3A454F" BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox x:Name="JobCheckBox" Grid.Row="0" Grid.Column="0" Margin="0,0,10,0" VerticalAlignment="Center" Tag="{Binding}" Checked="JobCheckBox_CheckedChanged" Unchecked="JobCheckBox_CheckedChanged"/>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding VideoName}" Foreground="White" FontWeight="SemiBold" TextTrimming="CharacterEllipsis" Margin="0,0,8,0"/>
|
||||
<TextBlock Text="{Binding Progress, StringFormat={}{0:0}%}" Foreground="{StaticResource TextSecondaryBrush}" FontSize="11"/>
|
||||
</StackPanel>
|
||||
<Button Grid.Row="0" Grid.Column="2" Content="✕" Width="30" Height="26" Style="{StaticResource SmallGhostButton}" Tag="{Binding}" Click="RemoveQueueItem_Click" ToolTip="Rimuovi"/>
|
||||
|
||||
<ProgressBar Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,8,0,0" Value="{Binding Progress}"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Text="{Binding StatusMessage}" Foreground="{StaticResource TextSecondaryBrush}" FontSize="11" Margin="0,6,0,0" TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" FontSize="10" Foreground="#87939F" Margin="0,8,0,0" TextWrapping="Wrap">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}📁 {0} 📐 {1} 🔄 {2} 🏷 {3} 🎯 {4}">
|
||||
<Binding Path="OutputFolderDisplay"/>
|
||||
<Binding Path="FrameSizeDisplay"/>
|
||||
<Binding Path="OverwriteModeDisplay"/>
|
||||
<Binding Path="NamingPatternDisplay"/>
|
||||
<Binding Path="ExtractionModeDisplay"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Pannello destro -->
|
||||
<StackPanel Grid.Column="1" Margin="8 12 18 12">
|
||||
<Border Background="{StaticResource PanelBrush}" BorderBrush="{StaticResource BorderBrushColor}" BorderThickness="1" CornerRadius="8" Padding="14">
|
||||
<StackPanel>
|
||||
<TextBlock Text="Impostazioni Globali" FontSize="16" FontWeight="SemiBold" Foreground="{StaticResource TextPrimaryBrush}"/>
|
||||
<TextBlock Text="Cartella Output" Foreground="{StaticResource TextSecondaryBrush}" FontSize="12" Margin="0,10,0,2"/>
|
||||
<DockPanel LastChildFill="True">
|
||||
<TextBox x:Name="GlobalOutputFolderTextBox" Height="34" Margin="0,0,10,0" IsReadOnly="True" Background="#2C333B" BorderBrush="#3A434C" Foreground="White" BorderThickness="1"/>
|
||||
<Button Content="Sfoglia" Width="80" Style="{StaticResource SmallGhostButton}" Click="SelectOutputFolderButton_Click"/>
|
||||
</DockPanel>
|
||||
<TextBlock Text="Anteprime (Thumbnails)" Foreground="{StaticResource TextSecondaryBrush}" FontSize="12" Margin="0,14,0,4"/>
|
||||
<Border Background="{StaticResource PanelSubBrush}" BorderBrush="#3A454F" BorderThickness="1" CornerRadius="6" Padding="6" Height="260">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="ThumbnailsPanel">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel IsItemsHost="True"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Margin="4" BorderBrush="#3D4853" BorderThickness="1" CornerRadius="4">
|
||||
<Image Source="{Binding}" Width="90" Height="52" Stretch="UniformToFill"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Right">
|
||||
<Button x:Name="StartQueueButton" Content="▶ Avvia" Width="90" Height="35" Margin="0,0,8,0"
|
||||
Click="StartQueueButton_Click" Background="#4FC3F7" Foreground="White" FontSize="12"/>
|
||||
<Button x:Name="StopQueueButton" Content="⏹ Stop" Width="90" Height="35"
|
||||
Click="StopQueueButton_Click" Background="#DC3545" Foreground="White" FontSize="12" IsEnabled="False"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
<!-- Secondary controls -->
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,0,0,10" HorizontalAlignment="Center">
|
||||
<Button x:Name="ConfigureSelectedButton" Content="⚙ Configura" Width="100" Height="30" Margin="0,0,8,0"
|
||||
Click="ConfigureSelectedButton_Click" FontSize="11" IsEnabled="False"/>
|
||||
<Button x:Name="ClearCompletedButton" Content="🗑 Pulisci Completati" Width="140" Height="30" Margin="0,0,8,0"
|
||||
Click="ClearCompletedButton_Click" FontSize="11"/>
|
||||
<Button x:Name="ClearAllButton" Content="🗑 Pulisci Tutto" Width="120" Height="30"
|
||||
Click="ClearAllButton_Click" Background="#6C757D" Foreground="White"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Queue List FULL AREA -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,10" HorizontalAlignment="Center">
|
||||
<TextBlock Text="Coda Elaborazione" FontSize="18" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="QueueCountText" Text="(0 elementi)" Foreground="#AAA" FontSize="12" Margin="10,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl x:Name="QueueItemsControl">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Background="#2C2C2C" Margin="0,0,0,8" Padding="10" CornerRadius="6" BorderBrush="#444" BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<CheckBox x:Name="JobCheckBox" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Margin="0,0,8,0"
|
||||
Checked="JobCheckBox_CheckedChanged" Unchecked="JobCheckBox_CheckedChanged" Tag="{Binding}"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding VideoName}" Foreground="White" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"/>
|
||||
<Button Grid.Row="0" Grid.Column="2" Content="×" Width="22" Height="22" FontSize="14"
|
||||
Click="RemoveQueueItem_Click" Background="Transparent" Foreground="#FF6B6B" BorderThickness="0"
|
||||
Tag="{Binding}" ToolTip="Rimuovi dalla coda"/>
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding StatusMessage}" Foreground="#AAA" FontSize="11" Margin="0,4,0,0"/>
|
||||
<ProgressBar Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3" Height="5" Margin="0,6,0,0" Value="{Binding Progress}" Background="#555" Foreground="#4FC3F7"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Text="{Binding Status}" Foreground="{Binding Status, Converter={StaticResource StatusColorConverter}}" FontSize="10" Margin="0,4,0,0"/>
|
||||
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" FontSize="9" Foreground="#666" Margin="0,4,0,0">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}📁 {0} | 📐 {1} | 🔄 {2} | 🏷 {3} | 🎯 {4}">
|
||||
<Binding Path="OutputFolderDisplay"/>
|
||||
<Binding Path="FrameSizeDisplay"/>
|
||||
<Binding Path="OverwriteModeDisplay"/>
|
||||
<Binding Path="NamingPatternDisplay"/>
|
||||
<Binding Path="ExtractionModeDisplay"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
|
||||
<!-- Bottom status and thumbnails (thumbnails optional) -->
|
||||
<DockPanel Grid.Row="3" Margin="0,10,0,0">
|
||||
<StackPanel DockPanel.Dock="Left" Width="400">
|
||||
<ProgressBar x:Name="ProgressBar" Height="20" Minimum="0" Maximum="100" Value="0" Background="#333" Foreground="#4FC3F7"/>
|
||||
<TextBlock x:Name="StatusText" Foreground="#AAA" FontSize="13" Margin="0,6,0,0" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<ScrollViewer DockPanel.Dock="Right" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Height="80">
|
||||
<ItemsControl x:Name="ThumbnailsPanel">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border Margin="2" BorderBrush="#555" BorderThickness="1" CornerRadius="4">
|
||||
<Image Source="{Binding}" Width="100" Height="56" Stretch="UniformToFill"/>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
<!-- BARRA STATO -->
|
||||
<Border Grid.Row="2" Background="#242A31" BorderBrush="#303840" BorderThickness="1,1,1,0" Padding="16 6" CornerRadius="6 6 0 0" Margin="14 0 14 14">
|
||||
<DockPanel>
|
||||
<TextBlock x:Name="StatusText" Foreground="{StaticResource TextSecondaryBrush}" FontSize="13" VerticalAlignment="Center" Text="Pronto"/>
|
||||
<TextBlock Text=" | " Foreground="#55606B" Margin="6,0"/>
|
||||
<TextBlock Text="Job:" Foreground="#77818B" Margin="0,0,4,0"/>
|
||||
<TextBlock x:Name="JobsSummaryText" Foreground="#4F5962" FontSize="11" VerticalAlignment="Center"/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
|
||||
@@ -19,9 +19,6 @@ using WpfButton = System.Windows.Controls.Button;
|
||||
|
||||
namespace Ganimede
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private string? outputFolder;
|
||||
@@ -40,28 +37,41 @@ namespace Ganimede
|
||||
{
|
||||
ThumbnailsPanel.ItemsSource = thumbnails;
|
||||
QueueItemsControl.ItemsSource = _processingService.JobQueue;
|
||||
|
||||
// Load saved settings
|
||||
|
||||
outputFolder = Settings.Default.LastOutputFolder;
|
||||
if (!string.IsNullOrEmpty(outputFolder))
|
||||
StatusText.Text = $"Output folder: {outputFolder}";
|
||||
{
|
||||
StatusText.Text = "Pronto";
|
||||
GlobalOutputFolderTextBox.Text = outputFolder;
|
||||
}
|
||||
|
||||
// Subscribe to processing service events
|
||||
_processingService.JobCompleted += OnJobCompleted;
|
||||
_processingService.JobFailed += OnJobFailed;
|
||||
_processingService.ProcessingStarted += OnProcessingStarted;
|
||||
_processingService.ProcessingStopped += OnProcessingStopped;
|
||||
_processingService.JobQueue.CollectionChanged += (s, e) => UpdateQueueCount();
|
||||
|
||||
Debug.WriteLine("[INIT] MainWindow initialized with queue support.");
|
||||
UpdateQueueCount();
|
||||
}
|
||||
|
||||
private void UpdateQueueCount()
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
var count = _processingService.JobQueue.Count;
|
||||
QueueCountText.Text = $"({count} elementi)";
|
||||
QueueCountText.Text = $"({_processingService.JobQueue.Count})";
|
||||
UpdateJobsSummary();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateJobsSummary()
|
||||
{
|
||||
var pending = _processingService.JobQueue.Count(j => j.Status == JobStatus.Pending);
|
||||
var processing = _processingService.JobQueue.Count(j => j.Status == JobStatus.Processing);
|
||||
var completed = _processingService.JobQueue.Count(j => j.Status == JobStatus.Completed);
|
||||
var failed = _processingService.JobQueue.Count(j => j.Status == JobStatus.Failed);
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
JobsSummaryText.Text = $"In attesa: {pending} | In corso: {processing} | Completati: {completed} | Falliti: {failed}";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,7 +81,8 @@ namespace Ganimede
|
||||
{
|
||||
StartQueueButton.IsEnabled = false;
|
||||
StopQueueButton.IsEnabled = true;
|
||||
StatusText.Text = "Elaborazione coda avviata...";
|
||||
StatusText.Text = "Elaborazione coda...";
|
||||
UpdateJobsSummary();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,7 +92,8 @@ namespace Ganimede
|
||||
{
|
||||
StartQueueButton.IsEnabled = true;
|
||||
StopQueueButton.IsEnabled = false;
|
||||
StatusText.Text = "Elaborazione coda fermata.";
|
||||
StatusText.Text = "Coda fermata";
|
||||
UpdateJobsSummary();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,8 +102,8 @@ namespace Ganimede
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
StatusText.Text = $"✓ Completato: {job.VideoName}";
|
||||
// Load thumbnails for the completed job
|
||||
LoadThumbnailsFromFolder(job.OutputFolder);
|
||||
UpdateJobsSummary();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -99,7 +111,8 @@ namespace Ganimede
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
StatusText.Text = $"✗ Fallito: {job.VideoName} - {job.StatusMessage}";
|
||||
StatusText.Text = $"✗ Fallito: {job.VideoName}";
|
||||
UpdateJobsSummary();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,206 +121,125 @@ namespace Ganimede
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(folder)) return;
|
||||
|
||||
var imageFiles = Directory.GetFiles(folder, "*.png")
|
||||
.OrderBy(f => f)
|
||||
.Take(20) // Load max 20 thumbnails for preview
|
||||
.ToList();
|
||||
|
||||
var imageFiles = Directory.GetFiles(folder, "*.png").OrderBy(f => f).Take(60).ToList();
|
||||
thumbnails.Clear();
|
||||
foreach (var imagePath in imageFiles)
|
||||
{
|
||||
var bitmap = new BitmapImage();
|
||||
bitmap.BeginInit();
|
||||
bitmap.UriSource = new Uri(imagePath);
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.EndInit();
|
||||
thumbnails.Add(bitmap);
|
||||
var bmp = new BitmapImage();
|
||||
bmp.BeginInit();
|
||||
bmp.UriSource = new Uri(imagePath);
|
||||
bmp.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bmp.EndInit();
|
||||
thumbnails.Add(bmp);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[ERROR] Failed to load thumbnails: {ex.Message}");
|
||||
Debug.WriteLine($"[ERROR] Impossibile caricare thumbnails: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigureFFMpeg()
|
||||
{
|
||||
var ffmpegBin = Settings.Default.FFmpegBinFolder;
|
||||
|
||||
if (!string.IsNullOrEmpty(ffmpegBin) && ValidateFFMpegBinaries(ffmpegBin))
|
||||
{
|
||||
FFMpegCore.GlobalFFOptions.Configure(options => options.BinaryFolder = ffmpegBin);
|
||||
Debug.WriteLine($"[CONFIG] FFMpeg bin folder set: {ffmpegBin}");
|
||||
}
|
||||
else if (TryUseSystemFFMpeg())
|
||||
{
|
||||
Debug.WriteLine("[CONFIG] Using system FFMpeg from PATH");
|
||||
}
|
||||
FFMpegCore.GlobalFFOptions.Configure(o => o.BinaryFolder = ffmpegBin);
|
||||
else if (TryUseSystemFFMpeg()) { }
|
||||
else if (TryFixMissingFFMpeg(ffmpegBin))
|
||||
{
|
||||
FFMpegCore.GlobalFFOptions.Configure(options => options.BinaryFolder = ffmpegBin);
|
||||
Debug.WriteLine($"[CONFIG] FFMpeg fixed and configured: {ffmpegBin}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine("[ERROR] FFMpeg configuration failed.");
|
||||
}
|
||||
FFMpegCore.GlobalFFOptions.Configure(o => o.BinaryFolder = ffmpegBin);
|
||||
}
|
||||
|
||||
private bool ValidateFFMpegBinaries(string binFolder)
|
||||
{
|
||||
if (!Directory.Exists(binFolder))
|
||||
return false;
|
||||
|
||||
var ffmpegPath = Path.Combine(binFolder, "ffmpeg.exe");
|
||||
var ffprobePath = Path.Combine(binFolder, "ffprobe.exe");
|
||||
|
||||
return File.Exists(ffmpegPath) && File.Exists(ffprobePath);
|
||||
}
|
||||
private bool ValidateFFMpegBinaries(string binFolder) =>
|
||||
Directory.Exists(binFolder) &&
|
||||
File.Exists(Path.Combine(binFolder, "ffmpeg.exe")) &&
|
||||
File.Exists(Path.Combine(binFolder, "ffprobe.exe"));
|
||||
|
||||
private bool TryUseSystemFFMpeg()
|
||||
{
|
||||
try
|
||||
{
|
||||
var processInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "ffmpeg",
|
||||
Arguments = "-version",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var process = Process.Start(processInfo);
|
||||
return process != null && process.WaitForExit(5000) && process.ExitCode == 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
var psi = new ProcessStartInfo { FileName = "ffmpeg", Arguments = "-version", UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true };
|
||||
using var p = Process.Start(psi);
|
||||
return p != null && p.WaitForExit(4000) && p.ExitCode == 0;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
private bool TryFixMissingFFMpeg(string binFolder)
|
||||
{
|
||||
if (string.IsNullOrEmpty(binFolder) || !Directory.Exists(binFolder))
|
||||
return false;
|
||||
|
||||
if (string.IsNullOrEmpty(binFolder) || !Directory.Exists(binFolder)) return false;
|
||||
var ffmpegPath = Path.Combine(binFolder, "ffmpeg.exe");
|
||||
var ffprobePath = Path.Combine(binFolder, "ffprobe.exe");
|
||||
|
||||
if (!File.Exists(ffmpegPath) && File.Exists(ffprobePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
Debug.WriteLine("[FIX] Attempting to copy ffprobe.exe as ffmpeg.exe");
|
||||
File.Copy(ffprobePath, ffmpegPath, true);
|
||||
return File.Exists(ffmpegPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[ERROR] Failed to copy ffprobe as ffmpeg: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
try { File.Copy(ffprobePath, ffmpegPath, true); return File.Exists(ffmpegPath); } catch { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void BrowseVideoButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Debug.WriteLine("[UI] BrowseVideoButton_Click invoked.");
|
||||
var dialog = new WpfOpenFileDialog
|
||||
{
|
||||
Filter = "Video files (*.mp4;*.avi;*.mov;*.mkv;*.wmv)|*.mp4;*.avi;*.mov;*.mkv;*.wmv|All files (*.*)|*.*",
|
||||
Multiselect = true
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
AddVideosToQueue(dialog.FileNames);
|
||||
}
|
||||
var dialog = new WpfOpenFileDialog { Filter = "Video (*.mp4;*.avi;*.mov;*.mkv;*.wmv)|*.mp4;*.avi;*.mov;*.mkv;*.wmv|Tutti i file (*.*)|*.*", Multiselect = true };
|
||||
if (dialog.ShowDialog() == true) AddVideosToQueue(dialog.FileNames);
|
||||
}
|
||||
|
||||
private void ImportFolderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||
{
|
||||
Description = "Seleziona la cartella contenente i video da importare",
|
||||
ShowNewFolderButton = false
|
||||
};
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog { Description = "Seleziona la cartella con i video", ShowNewFolderButton = false };
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
var folder = dialog.SelectedPath;
|
||||
try
|
||||
{
|
||||
var videoFiles = Directory.EnumerateFiles(folder, "*.*", SearchOption.TopDirectoryOnly)
|
||||
.Where(IsVideoFile)
|
||||
.ToArray();
|
||||
if (videoFiles.Length == 0)
|
||||
var files = Directory.EnumerateFiles(dialog.SelectedPath, "*.*", SearchOption.TopDirectoryOnly).Where(IsVideoFile).ToArray();
|
||||
if (files.Length == 0)
|
||||
{
|
||||
WpfMessageBox.Show("Nessun file video valido trovato nella cartella.", "Importa Cartella", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
WpfMessageBox.Show("Nessun file video valido trovato.", "Importa Cartella", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
AddVideosToQueue(videoFiles);
|
||||
StatusText.Text = $"Importati {videoFiles.Length} video dalla cartella.";
|
||||
AddVideosToQueue(files);
|
||||
StatusText.Text = $"Importati {files.Length} video.";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WpfMessageBox.Show($"Errore durante l'importazione: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
WpfMessageBox.Show($"Errore: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddVideosToQueue(string[] videoPaths)
|
||||
private void AddVideosToQueue(string[] paths)
|
||||
{
|
||||
if (string.IsNullOrEmpty(outputFolder))
|
||||
{
|
||||
WpfMessageBox.Show("Seleziona prima una cartella di output.", "Cartella Output Richiesta",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
WpfMessageBox.Show("Seleziona prima una cartella di output.", "Cartella Output Richiesta", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
var createSubfolder = Settings.Default.CreateSubfolder;
|
||||
|
||||
foreach (var videoPath in videoPaths)
|
||||
{
|
||||
_processingService.AddJob(videoPath, outputFolder, createSubfolder);
|
||||
Debug.WriteLine($"[QUEUE] Added video to queue: {Path.GetFileName(videoPath)}");
|
||||
}
|
||||
|
||||
StatusText.Text = $"Aggiunti {videoPaths.Length} video in coda (Pending)";
|
||||
Settings.Default.LastVideoPath = videoPaths.FirstOrDefault();
|
||||
var createSub = Settings.Default.CreateSubfolder;
|
||||
foreach (var p in paths) _processingService.AddJob(p, outputFolder, createSub);
|
||||
StatusText.Text = $"Aggiunti {paths.Length} video (In attesa)";
|
||||
Settings.Default.LastVideoPath = paths.FirstOrDefault();
|
||||
Settings.Default.Save();
|
||||
UpdateQueueCount();
|
||||
}
|
||||
|
||||
private void SelectOutputFolderButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Debug.WriteLine("[UI] SelectOutputFolderButton_Click invoked.");
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog();
|
||||
|
||||
if (!string.IsNullOrEmpty(outputFolder))
|
||||
dialog.SelectedPath = outputFolder;
|
||||
|
||||
if (!string.IsNullOrEmpty(outputFolder)) dialog.SelectedPath = outputFolder;
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
outputFolder = dialog.SelectedPath;
|
||||
StatusText.Text = $"Output folder: {outputFolder}";
|
||||
GlobalOutputFolderTextBox.Text = outputFolder;
|
||||
StatusText.Text = "Cartella output aggiornata";
|
||||
Settings.Default.LastOutputFolder = outputFolder;
|
||||
Settings.Default.Save();
|
||||
Debug.WriteLine($"[INFO] Output folder selected: {outputFolder}");
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var settingsWindow = new SettingsWindow
|
||||
var win = new SettingsWindow { Owner = this };
|
||||
if (win.ShowDialog() == true)
|
||||
{
|
||||
Owner = this
|
||||
};
|
||||
|
||||
if (settingsWindow.ShowDialog() == true)
|
||||
{
|
||||
// Reconfigure FFMpeg if settings changed
|
||||
ConfigureFFMpeg();
|
||||
StatusText.Text = "Impostazioni aggiornate";
|
||||
}
|
||||
@@ -317,97 +249,59 @@ namespace Ganimede
|
||||
{
|
||||
if (_processingService.JobQueue.Count == 0)
|
||||
{
|
||||
WpfMessageBox.Show("Nessun video in coda. Aggiungi prima dei video.", "Coda Vuota",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
WpfMessageBox.Show("Nessun video in coda.", "Coda Vuota", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var pendingJobs = _processingService.JobQueue.Count(job => job.Status == JobStatus.Pending);
|
||||
if (pendingJobs == 0)
|
||||
if (_processingService.JobQueue.All(j => j.Status != JobStatus.Pending))
|
||||
{
|
||||
WpfMessageBox.Show("Nessun job in stato Pending.", "Nessun Job",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
WpfMessageBox.Show("Nessun job in stato In attesa.", "Nessun Job", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.WriteLine("[QUEUE] Starting queue processing manually");
|
||||
await _processingService.StartProcessingAsync();
|
||||
UpdateJobsSummary();
|
||||
}
|
||||
|
||||
private void StopQueueButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_processingService.StopProcessing();
|
||||
StatusText.Text = "Arresto elaborazione in corso...";
|
||||
Debug.WriteLine("[QUEUE] Stop processing requested by user");
|
||||
StatusText.Text = "Arresto in corso...";
|
||||
UpdateJobsSummary();
|
||||
}
|
||||
|
||||
private void JobCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UpdateSelectedJobs();
|
||||
}
|
||||
private void JobCheckBox_CheckedChanged(object sender, RoutedEventArgs e) => UpdateSelectedJobs();
|
||||
|
||||
private void UpdateSelectedJobs()
|
||||
{
|
||||
_selectedJobs.Clear();
|
||||
|
||||
// Find all checked checkboxes in the ItemsControl
|
||||
var checkBoxes = FindVisualChildren<System.Windows.Controls.CheckBox>(QueueItemsControl).Where(cb => cb.Name == "JobCheckBox");
|
||||
|
||||
foreach (var checkBox in checkBoxes)
|
||||
{
|
||||
if (checkBox.IsChecked == true && checkBox.Tag is VideoJob job)
|
||||
{
|
||||
_selectedJobs.Add(job);
|
||||
}
|
||||
}
|
||||
|
||||
// Update configure button state
|
||||
var boxes = FindVisualChildren<System.Windows.Controls.CheckBox>(QueueItemsControl).Where(cb => cb.Name == "JobCheckBox");
|
||||
foreach (var cb in boxes) if (cb.IsChecked == true && cb.Tag is VideoJob job) _selectedJobs.Add(job);
|
||||
ConfigureSelectedButton.IsEnabled = _selectedJobs.Count > 0;
|
||||
|
||||
Debug.WriteLine($"[SELECTION] {_selectedJobs.Count} jobs selected");
|
||||
}
|
||||
|
||||
private void ConfigureSelectedButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_selectedJobs.Count == 0)
|
||||
return;
|
||||
|
||||
var configWindow = new JobConfigWindow(_selectedJobs.ToList())
|
||||
if (_selectedJobs.Count == 0) return;
|
||||
var cfg = new JobConfigWindow(_selectedJobs.ToList()) { Owner = this };
|
||||
if (cfg.ShowDialog() == true)
|
||||
{
|
||||
Owner = this
|
||||
};
|
||||
|
||||
if (configWindow.ShowDialog() == true)
|
||||
{
|
||||
StatusText.Text = $"Configurazione applicata a {_selectedJobs.Count} job(s)";
|
||||
|
||||
// Reset job output folders for those without custom settings
|
||||
StatusText.Text = $"Configurazione applicata a {_selectedJobs.Count} job";
|
||||
foreach (var job in _selectedJobs.Where(j => string.IsNullOrEmpty(j.CustomOutputFolder)))
|
||||
{
|
||||
var createSubfolder = Settings.Default.CreateSubfolder;
|
||||
if (job.ExtractionMode == ExtractionMode.SingleFrame)
|
||||
{
|
||||
job.OutputFolder = outputFolder; // single frame directly
|
||||
}
|
||||
else
|
||||
{
|
||||
job.OutputFolder = createSubfolder
|
||||
? Path.Combine(outputFolder, job.VideoName)
|
||||
: outputFolder;
|
||||
}
|
||||
var createSub = Settings.Default.CreateSubfolder;
|
||||
job.OutputFolder = job.ExtractionMode == ExtractionMode.SingleFrame ? outputFolder : (createSub ? Path.Combine(outputFolder, job.VideoName) : outputFolder);
|
||||
}
|
||||
UpdateJobsSummary();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveQueueItem_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is WpfButton button && button.Tag is VideoJob job)
|
||||
if (sender is WpfButton btn && btn.Tag is VideoJob job)
|
||||
{
|
||||
_processingService.CancelJob(job);
|
||||
if (job.Status == JobStatus.Cancelled || job.Status == JobStatus.Pending)
|
||||
{
|
||||
_processingService.JobQueue.Remove(job);
|
||||
}
|
||||
UpdateQueueCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,83 +309,55 @@ namespace Ganimede
|
||||
{
|
||||
_processingService.RemoveCompletedJobs();
|
||||
StatusText.Text = "Job completati rimossi";
|
||||
UpdateQueueCount();
|
||||
}
|
||||
|
||||
private void ClearAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var processingJobs = _processingService.JobQueue.Where(j => j.Status == JobStatus.Processing).ToList();
|
||||
|
||||
if (processingJobs.Count > 0)
|
||||
var processing = _processingService.JobQueue.Any(j => j.Status == JobStatus.Processing);
|
||||
if (processing)
|
||||
{
|
||||
var result = WpfMessageBox.Show(
|
||||
$"Ci sono {processingJobs.Count} job in elaborazione.\n\n" +
|
||||
"Sì: Ferma tutto e svuota la coda\n" +
|
||||
"No: Rimuovi solo job completati/pending\n" +
|
||||
"Annulla: Non fare nulla",
|
||||
"Job in corso",
|
||||
MessageBoxButton.YesNoCancel,
|
||||
MessageBoxImage.Question);
|
||||
|
||||
switch (result)
|
||||
var res = WpfMessageBox.Show("Ci sono job in elaborazione.\n\nSi: Ferma e svuota la coda\nNo: Rimuovi solo job non in elaborazione\nAnnulla: Annulla", "Conferma", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
|
||||
if (res == MessageBoxResult.Cancel) return;
|
||||
if (res == MessageBoxResult.Yes)
|
||||
{
|
||||
case MessageBoxResult.Yes:
|
||||
_processingService.StopProcessing();
|
||||
_processingService.JobQueue.Clear();
|
||||
thumbnails.Clear();
|
||||
StatusText.Text = "Tutti i job rimossi e elaborazione fermata";
|
||||
break;
|
||||
case MessageBoxResult.No:
|
||||
for (int i = _processingService.JobQueue.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_processingService.JobQueue[i].Status != JobStatus.Processing)
|
||||
{
|
||||
_processingService.JobQueue.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
StatusText.Text = "Coda ripulita (job in corso mantenuti)";
|
||||
break;
|
||||
case MessageBoxResult.Cancel:
|
||||
return;
|
||||
_processingService.StopProcessing();
|
||||
_processingService.JobQueue.Clear();
|
||||
thumbnails.Clear();
|
||||
}
|
||||
else if (res == MessageBoxResult.No)
|
||||
{
|
||||
for (int i = _processingService.JobQueue.Count - 1; i >= 0; i--)
|
||||
if (_processingService.JobQueue[i].Status != JobStatus.Processing)
|
||||
_processingService.JobQueue.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = WpfMessageBox.Show("Sicuro di voler rimuovere tutti i job?",
|
||||
"Pulisci Tutto", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
if (WpfMessageBox.Show("Rimuovere tutti i job?", "Conferma", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes)
|
||||
{
|
||||
_processingService.JobQueue.Clear();
|
||||
thumbnails.Clear();
|
||||
StatusText.Text = "Tutti i job rimossi";
|
||||
}
|
||||
}
|
||||
StatusText.Text = "Coda aggiornata";
|
||||
UpdateQueueCount();
|
||||
}
|
||||
|
||||
private static bool IsVideoFile(string filePath)
|
||||
private static bool IsVideoFile(string path)
|
||||
{
|
||||
var extension = Path.GetExtension(filePath).ToLowerInvariant();
|
||||
return extension is ".mp4" or ".avi" or ".mov" or ".mkv" or ".wmv" or ".flv" or ".webm";
|
||||
var ext = Path.GetExtension(path).ToLowerInvariant();
|
||||
return ext is ".mp4" or ".avi" or ".mov" or ".mkv" or ".wmv" or ".flv" or ".webm";
|
||||
}
|
||||
|
||||
// Helper method to find visual children
|
||||
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
|
||||
private static IEnumerable<T> FindVisualChildren<T>(DependencyObject dep) where T : DependencyObject
|
||||
{
|
||||
if (depObj != null)
|
||||
if (dep == null) yield break;
|
||||
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(dep); i++)
|
||||
{
|
||||
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(depObj); i++)
|
||||
{
|
||||
DependencyObject child = System.Windows.Media.VisualTreeHelper.GetChild(depObj, i);
|
||||
if (child != null && child is T)
|
||||
{
|
||||
yield return (T)child;
|
||||
}
|
||||
|
||||
foreach (T childOfChild in FindVisualChildren<T>(child))
|
||||
{
|
||||
yield return childOfChild;
|
||||
}
|
||||
}
|
||||
var child = System.Windows.Media.VisualTreeHelper.GetChild(dep, i);
|
||||
if (child is T t) yield return t;
|
||||
foreach (var c in FindVisualChildren<T>(child)) yield return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,131 +4,183 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="Configure Selected Jobs" Height="560" Width="600"
|
||||
Background="#222" WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="20">
|
||||
Title="Configurazione Job" Height="640" Width="640"
|
||||
Background="#1E2228" WindowStartupLocation="CenterOwner">
|
||||
<Window.Resources>
|
||||
<SolidColorBrush x:Key="PanelBrush" Color="#242A31"/>
|
||||
<SolidColorBrush x:Key="PanelSubBrush" Color="#2C333B"/>
|
||||
<SolidColorBrush x:Key="BorderBrushColor" Color="#38424D"/>
|
||||
<SolidColorBrush x:Key="AccentBrush" Color="#268BFF"/>
|
||||
<Style TargetType="GroupBox">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="GroupBox">
|
||||
<Border Background="{StaticResource PanelBrush}" BorderBrush="{StaticResource BorderBrushColor}" BorderThickness="1" CornerRadius="8" Padding="12" >
|
||||
<DockPanel>
|
||||
<Border Background="#2C333B" CornerRadius="4" Padding="6 4" Margin="0 0 0 10" DockPanel.Dock="Top">
|
||||
<ContentPresenter ContentSource="Header"/>
|
||||
</Border>
|
||||
<ContentPresenter/>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="Button" x:Key="PrimaryButton">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Background" Value="#268BFF"/>
|
||||
<Setter Property="BorderBrush" Value="#1776DF"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="14 8"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="6">
|
||||
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#3595FF"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="True">
|
||||
<Setter Property="Background" Value="#1570D4"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter Property="Opacity" Value="0.45"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style TargetType="Button" x:Key="FlatButton" BasedOn="{StaticResource PrimaryButton}">
|
||||
<Setter Property="Background" Value="#2F3740"/>
|
||||
<Setter Property="BorderBrush" Value="#404B55"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter Property="Background" Value="#38434D"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Background" Value="#2C333B"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#3A444E"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="6 4"/>
|
||||
</Style>
|
||||
<Style TargetType="ComboBox">
|
||||
<Setter Property="Background" Value="#2C333B"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="BorderBrush" Value="#3A444E"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Padding" Value="4 2"/>
|
||||
</Style>
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Margin" Value="0 2 0 6"/>
|
||||
</Style>
|
||||
<Style TargetType="RadioButton">
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
<Setter Property="Margin" Value="0 0 14 0"/>
|
||||
</Style>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid Margin="18">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,20">
|
||||
<TextBlock Text="Configure Selected Jobs" FontSize="20" FontWeight="Bold" Foreground="White"/>
|
||||
<TextBlock x:Name="SelectedJobsText" Text="2 jobs selected" FontSize="12" Foreground="#AAA" Margin="0,5,0,0"/>
|
||||
<StackPanel Grid.Row="0" Margin="0 0 0 14">
|
||||
<TextBlock Text="Configurazione Job" FontSize="22" FontWeight="SemiBold" Foreground="White"/>
|
||||
<TextBlock x:Name="SelectedJobsText" Text="0 job selezionati" Foreground="#9BA5AF" FontSize="12" Margin="0 4 0 0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Settings Content -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<!-- Extraction Mode Settings -->
|
||||
<GroupBox Header="Modalita Estrazione" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock Text="Seleziona come estrarre i frame:" Foreground="#CCC" Margin="0,0,0,8"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
|
||||
<RadioButton x:Name="ExtractionFullRadio" Content="Completa" GroupName="ExtractionMode" IsChecked="True" Foreground="White" Margin="0,0,15,0"/>
|
||||
<RadioButton x:Name="ExtractionSingleRadio" Content="Singolo Frame" GroupName="ExtractionMode" Foreground="White" Margin="0,0,15,0"/>
|
||||
<RadioButton x:Name="ExtractionAutoRadio" Content="Auto" GroupName="ExtractionMode" Foreground="White"/>
|
||||
<!-- Output -->
|
||||
<GroupBox Header="Cartella di Output (Override facoltativo)" Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="UseCustomOutputCheckBox" Content="Usa cartella di output personalizzata" Checked="UseCustomOutputCheckBox_CheckedChanged" Unchecked="UseCustomOutputCheckBox_CheckedChanged"/>
|
||||
<DockPanel Margin="0 4 0 0" IsEnabled="{Binding IsChecked, ElementName=UseCustomOutputCheckBox}">
|
||||
<TextBox x:Name="CustomOutputTextBox" Height="34" VerticalContentAlignment="Center" DockPanel.Dock="Left" MinWidth="320" Margin="0 0 10 0"/>
|
||||
<Button x:Name="BrowseCustomOutputButton" Content="Sfoglia" Width="90" Height="34" Style="{StaticResource FlatButton}" Click="BrowseCustomOutputButton_Click"/>
|
||||
</DockPanel>
|
||||
<CheckBox x:Name="CreateSubfolderCheckBox" Content="Crea sottocartella per ogni video" IsChecked="True"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Extraction Mode -->
|
||||
<GroupBox Header="Modalita di Estrazione" Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||
<RadioButton x:Name="ExtractionFullRadio" Content="Completa" GroupName="ExtractionMode" IsChecked="True"/>
|
||||
<RadioButton x:Name="ExtractionSingleRadio" Content="Singolo Frame" GroupName="ExtractionMode"/>
|
||||
<RadioButton x:Name="ExtractionAutoRadio" Content="Auto" GroupName="ExtractionMode"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="Auto: il sistema analizza il video e decide se conviene estrarre tutti i frame o solo uno (tipico dei video social con immagine statica)." TextWrapping="Wrap" Foreground="#888" FontSize="11"/>
|
||||
<TextBlock Text="Auto: analizza il video e decide se estrarre tutti i frame o solo uno." Foreground="#8D969F" FontSize="11" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Output Settings -->
|
||||
<GroupBox Header="Output Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<CheckBox x:Name="UseCustomOutputCheckBox" Content="Use custom output folder for selected jobs"
|
||||
Foreground="White" Margin="0,0,0,10" Checked="UseCustomOutputCheckBox_CheckedChanged" Unchecked="UseCustomOutputCheckBox_CheckedChanged"/>
|
||||
|
||||
<Grid IsEnabled="{Binding IsChecked, ElementName=UseCustomOutputCheckBox}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="CustomOutputTextBox" Grid.Column="0" Height="30" VerticalContentAlignment="Center"
|
||||
Background="#333" Foreground="White" BorderBrush="#555" Margin="0,0,10,0"/>
|
||||
<Button x:Name="BrowseCustomOutputButton" Grid.Column="1" Content="Browse" Width="80" Height="30"
|
||||
Click="BrowseCustomOutputButton_Click"/>
|
||||
</Grid>
|
||||
|
||||
<CheckBox x:Name="CreateSubfolderCheckBox" Content="Create subfolder for each video"
|
||||
Foreground="White" IsChecked="True" Margin="0,10,0,0"/>
|
||||
<!-- Naming Pattern -->
|
||||
<GroupBox Header="Pattern Nome File (Override facoltativo)" Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="UseCustomNamingCheckBox" Content="Usa pattern di naming personalizzato" Checked="UseCustomNamingCheckBox_CheckedChanged" Unchecked="UseCustomNamingCheckBox_CheckedChanged"/>
|
||||
<ComboBox x:Name="CustomNamingComboBox" Height="32" Margin="0 6 0 0" SelectionChanged="CustomNamingComboBox_SelectionChanged" IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}">
|
||||
<ComboBoxItem Content="NomeVideo + Progressivo" Tag="VideoNameProgressive"/>
|
||||
<ComboBoxItem Content="Frame + Progressivo" Tag="FrameProgressive"/>
|
||||
<ComboBoxItem Content="NomeVideo + Timestamp" Tag="VideoNameTimestamp"/>
|
||||
<ComboBoxItem Content="Prefisso Custom + Progressivo" Tag="CustomProgressive"/>
|
||||
<ComboBoxItem Content="Solo Timestamp" Tag="TimestampOnly"/>
|
||||
<ComboBoxItem Content="NomeVideo + Frame + Progressivo" Tag="VideoNameFrameProgressive"/>
|
||||
</ComboBox>
|
||||
<StackPanel Orientation="Horizontal" Margin="0 6 0 0" IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}">
|
||||
<TextBlock Text="Prefisso:" Foreground="#9BA5AF" VerticalAlignment="Center" Margin="0 0 8 0"/>
|
||||
<TextBox x:Name="CustomNamingPrefixTextBox" Width="160" Text="custom" Height="30" VerticalContentAlignment="Center" TextChanged="CustomNamingPrefixTextBox_TextChanged"/>
|
||||
</StackPanel>
|
||||
<Border Background="#2F3740" CornerRadius="4" Padding="6" Margin="0 6 0 0" IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}">
|
||||
<TextBlock x:Name="JobNamingPreviewText" Text="Video1_000001.png" FontFamily="Consolas" Foreground="#4FA6FF" FontSize="12"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Frame Settings -->
|
||||
<GroupBox Header="Frame Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<CheckBox x:Name="UseCustomFrameSizeCheckBox" Content="Use custom frame size for selected jobs"
|
||||
Foreground="White" Margin="0,0,0,10" Checked="UseCustomFrameSizeCheckBox_CheckedChanged" Unchecked="UseCustomFrameSizeCheckBox_CheckedChanged"/>
|
||||
|
||||
<ComboBox x:Name="CustomFrameSizeComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomFrameSizeCheckBox}">
|
||||
<ComboBoxItem Content="Original Size (Keep aspect ratio)" Tag="original" IsSelected="True" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="320x180 (Fast)" Tag="320,180" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="640x360 (Medium)" Tag="640,360" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="1280x720 (High)" Tag="1280,720" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="1920x1080 (Full HD)" Tag="1920,1080" Foreground="Black" Background="White"/>
|
||||
<!-- Overwrite -->
|
||||
<GroupBox Header="Modalita Sovrascrittura (Override facoltativo)" Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="UseCustomOverwriteCheckBox" Content="Usa comportamento di sovrascrittura personalizzato" Checked="UseCustomOverwriteCheckBox_CheckedChanged" Unchecked="UseCustomOverwriteCheckBox_CheckedChanged"/>
|
||||
<ComboBox x:Name="CustomOverwriteComboBox" Height="32" Margin="0 6 0 0" IsEnabled="{Binding IsChecked, ElementName=UseCustomOverwriteCheckBox}">
|
||||
<ComboBoxItem Content="Chiedi" Tag="Ask"/>
|
||||
<ComboBoxItem Content="Salta" Tag="Skip"/>
|
||||
<ComboBoxItem Content="Sovrascrivi" Tag="Overwrite"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Overwrite Settings -->
|
||||
<GroupBox Header="File Overwrite Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<CheckBox x:Name="UseCustomOverwriteCheckBox" Content="Use custom overwrite behavior for selected jobs"
|
||||
Foreground="White" Margin="0,0,0,10" Checked="UseCustomOverwriteCheckBox_CheckedChanged" Unchecked="UseCustomOverwriteCheckBox_CheckedChanged"/>
|
||||
|
||||
<ComboBox x:Name="CustomOverwriteComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomOverwriteCheckBox}">
|
||||
<ComboBoxItem Content="Ask each time" Tag="Ask" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Skip existing files" Tag="Skip" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Overwrite existing files" Tag="Overwrite" Foreground="Black" Background="White"/>
|
||||
<!-- Frame size -->
|
||||
<GroupBox Header="Ridimensionamento Frame (Override facoltativo)" Margin="0,0,0,14">
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="UseCustomFrameSizeCheckBox" Content="Usa dimensione frame personalizzata" Checked="UseCustomFrameSizeCheckBox_CheckedChanged" Unchecked="UseCustomFrameSizeCheckBox_CheckedChanged"/>
|
||||
<ComboBox x:Name="CustomFrameSizeComboBox" Height="32" Margin="0 6 0 0" IsEnabled="{Binding IsChecked, ElementName=UseCustomFrameSizeCheckBox}">
|
||||
<ComboBoxItem Content="Risoluzione Originale" Tag="original"/>
|
||||
<ComboBoxItem Content="320x180 (Veloce)" Tag="320,180"/>
|
||||
<ComboBoxItem Content="640x360 (Media)" Tag="640,360"/>
|
||||
<ComboBoxItem Content="1280x720 (HD)" Tag="1280,720"/>
|
||||
<ComboBoxItem Content="1920x1080 (Full HD)" Tag="1920,1080"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Naming Settings -->
|
||||
<GroupBox Header="File Naming Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<CheckBox x:Name="UseCustomNamingCheckBox" Content="Use custom naming pattern for selected jobs"
|
||||
Foreground="White" Margin="0,0,0,10" Checked="UseCustomNamingCheckBox_CheckedChanged" Unchecked="UseCustomNamingCheckBox_CheckedChanged"/>
|
||||
|
||||
<TextBlock Text="Naming Pattern:" Foreground="#CCC" Margin="0,0,0,5"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}"/>
|
||||
<ComboBox x:Name="CustomNamingComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}" SelectionChanged="CustomNamingComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="VideoName + Progressive (recommended)" Tag="VideoNameProgressive" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Frame + Progressive" Tag="FrameProgressive" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="VideoName + Timestamp" Tag="VideoNameTimestamp" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Custom Prefix + Progressive" Tag="CustomProgressive" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Timestamp Only" Tag="TimestampOnly" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="VideoName + Frame + Progressive" Tag="VideoNameFrameProgressive" Foreground="Black" Background="White"/>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Text="Custom Prefix:" Foreground="#CCC" Margin="0,10,0,5"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}"/>
|
||||
<TextBox x:Name="CustomNamingPrefixTextBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
VerticalContentAlignment="Center" Text="custom"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}" TextChanged="CustomNamingPrefixTextBox_TextChanged"/>
|
||||
|
||||
<TextBlock Text="Preview:" Foreground="#777" FontSize="11" Margin="0,8,0,2"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}"/>
|
||||
<TextBlock x:Name="JobNamingPreviewText" Text="Video1_000001.png" Foreground="#4FC3F7" FontSize="10"
|
||||
FontFamily="Consolas" Background="#1A1A1A" Padding="3"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomNamingCheckBox}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
|
||||
<Button x:Name="ApplyButton" Content="Apply to Selected" Width="120" Height="35" Margin="0,0,10,0"
|
||||
Click="ApplyButton_Click" Background="#4FC3F7" Foreground="White" BorderThickness="0"/>
|
||||
<Button x:Name="CancelButton" Content="Cancel" Width="80" Height="35"
|
||||
Click="CancelButton_Click"/>
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 16 0 0">
|
||||
<Button x:Name="ApplyButton" Content="Applica" Width="140" Height="42" Style="{StaticResource PrimaryButton}" Click="ApplyButton_Click" Margin="0,0,12,0"/>
|
||||
<Button x:Name="CancelButton" Content="Annulla" Width="110" Height="42" Style="{StaticResource FlatButton}" Click="CancelButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -17,32 +17,21 @@ namespace Ganimede.Windows
|
||||
{
|
||||
InitializeComponent();
|
||||
_selectedJobs = selectedJobs;
|
||||
|
||||
SelectedJobsText.Text = $"{selectedJobs.Count} job(s) selected";
|
||||
SelectedJobsText.Text = $"{selectedJobs.Count} job selezionati";
|
||||
LoadCurrentSettings();
|
||||
}
|
||||
|
||||
private void SetExtractionModeRadio(ExtractionMode mode)
|
||||
{
|
||||
var fullObj = FindName("ExtractionFullRadio");
|
||||
if (fullObj is System.Windows.Controls.RadioButton fullRb)
|
||||
fullRb.IsChecked = mode == ExtractionMode.Full;
|
||||
var singleObj = FindName("ExtractionSingleRadio");
|
||||
if (singleObj is System.Windows.Controls.RadioButton singleRb)
|
||||
singleRb.IsChecked = mode == ExtractionMode.SingleFrame;
|
||||
var autoObj = FindName("ExtractionAutoRadio");
|
||||
if (autoObj is System.Windows.Controls.RadioButton autoRb)
|
||||
autoRb.IsChecked = mode == ExtractionMode.Auto;
|
||||
if (FindName("ExtractionFullRadio") is System.Windows.Controls.RadioButton fullRb) fullRb.IsChecked = mode == ExtractionMode.Full;
|
||||
if (FindName("ExtractionSingleRadio") is System.Windows.Controls.RadioButton singleRb) singleRb.IsChecked = mode == ExtractionMode.SingleFrame;
|
||||
if (FindName("ExtractionAutoRadio") is System.Windows.Controls.RadioButton autoRb) autoRb.IsChecked = mode == ExtractionMode.Auto;
|
||||
}
|
||||
|
||||
private ExtractionMode GetSelectedExtractionMode()
|
||||
{
|
||||
var singleObj = FindName("ExtractionSingleRadio");
|
||||
if (singleObj is System.Windows.Controls.RadioButton singleRb && singleRb.IsChecked == true)
|
||||
return ExtractionMode.SingleFrame;
|
||||
var autoObj = FindName("ExtractionAutoRadio");
|
||||
if (autoObj is System.Windows.Controls.RadioButton autoRb && autoRb.IsChecked == true)
|
||||
return ExtractionMode.Auto;
|
||||
if (FindName("ExtractionSingleRadio") is System.Windows.Controls.RadioButton singleRb && singleRb.IsChecked == true) return ExtractionMode.SingleFrame;
|
||||
if (FindName("ExtractionAutoRadio") is System.Windows.Controls.RadioButton autoRb && autoRb.IsChecked == true) return ExtractionMode.Auto;
|
||||
return ExtractionMode.Full;
|
||||
}
|
||||
|
||||
@@ -52,8 +41,6 @@ namespace Ganimede.Windows
|
||||
if (firstJob != null)
|
||||
{
|
||||
SetExtractionModeRadio(firstJob.ExtractionMode);
|
||||
|
||||
// Output settings
|
||||
if (!string.IsNullOrEmpty(firstJob.CustomOutputFolder))
|
||||
{
|
||||
UseCustomOutputCheckBox.IsChecked = true;
|
||||
@@ -61,7 +48,6 @@ namespace Ganimede.Windows
|
||||
}
|
||||
CreateSubfolderCheckBox.IsChecked = firstJob.CustomCreateSubfolder;
|
||||
|
||||
// Frame size settings
|
||||
if (!string.IsNullOrEmpty(firstJob.CustomFrameSize))
|
||||
{
|
||||
UseCustomFrameSizeCheckBox.IsChecked = true;
|
||||
@@ -75,7 +61,6 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite settings
|
||||
if (firstJob.CustomOverwriteMode.HasValue)
|
||||
{
|
||||
UseCustomOverwriteCheckBox.IsChecked = true;
|
||||
@@ -89,7 +74,6 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
// Naming settings
|
||||
if (firstJob.CustomNamingPattern.HasValue)
|
||||
{
|
||||
UseCustomNamingCheckBox.IsChecked = true;
|
||||
@@ -105,12 +89,9 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
if (CustomFrameSizeComboBox.SelectedItem == null)
|
||||
CustomFrameSizeComboBox.SelectedIndex = 0;
|
||||
if (CustomOverwriteComboBox.SelectedItem == null)
|
||||
CustomOverwriteComboBox.SelectedIndex = 0;
|
||||
if (CustomNamingComboBox.SelectedItem == null)
|
||||
CustomNamingComboBox.SelectedIndex = 0;
|
||||
if (CustomFrameSizeComboBox.SelectedItem == null) CustomFrameSizeComboBox.SelectedIndex = 0;
|
||||
if (CustomOverwriteComboBox.SelectedItem == null) CustomOverwriteComboBox.SelectedIndex = 0;
|
||||
if (CustomNamingComboBox.SelectedItem == null) CustomNamingComboBox.SelectedIndex = 0;
|
||||
|
||||
UpdateJobNamingPreview();
|
||||
}
|
||||
@@ -130,7 +111,7 @@ namespace Ganimede.Windows
|
||||
}
|
||||
else
|
||||
{
|
||||
JobNamingPreviewText.Text = "Video1_000001.png (using default)";
|
||||
JobNamingPreviewText.Text = "Video1_000001.png (default)";
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -148,19 +129,9 @@ namespace Ganimede.Windows
|
||||
|
||||
private void BrowseCustomOutputButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||
{
|
||||
Description = "Select custom output folder for selected jobs",
|
||||
ShowNewFolderButton = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(CustomOutputTextBox.Text))
|
||||
dialog.SelectedPath = CustomOutputTextBox.Text;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
CustomOutputTextBox.Text = dialog.SelectedPath;
|
||||
}
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog { Description = "Seleziona cartella di output personalizzata", ShowNewFolderButton = true };
|
||||
if (!string.IsNullOrEmpty(CustomOutputTextBox.Text)) dialog.SelectedPath = CustomOutputTextBox.Text;
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) CustomOutputTextBox.Text = dialog.SelectedPath;
|
||||
}
|
||||
|
||||
private void ApplyButton_Click(object sender, RoutedEventArgs e)
|
||||
@@ -176,50 +147,28 @@ namespace Ganimede.Windows
|
||||
{
|
||||
job.CustomOutputFolder = CustomOutputTextBox.Text;
|
||||
job.CustomCreateSubfolder = CreateSubfolderCheckBox.IsChecked ?? true;
|
||||
// Do NOT create per-video folder if SingleFrame
|
||||
if (job.CustomCreateSubfolder && job.ExtractionMode != ExtractionMode.SingleFrame)
|
||||
{
|
||||
job.OutputFolder = System.IO.Path.Combine(job.CustomOutputFolder, job.VideoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
job.OutputFolder = job.CustomOutputFolder; // single frame goes directly here
|
||||
}
|
||||
job.OutputFolder = job.CustomOutputFolder;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.CustomOutputFolder = string.Empty;
|
||||
// OutputFolder will be recalculated in MainWindow if needed
|
||||
}
|
||||
|
||||
if (UseCustomFrameSizeCheckBox.IsChecked == true && CustomFrameSizeComboBox.SelectedItem is ComboBoxItem frameSizeItem)
|
||||
{
|
||||
job.CustomFrameSize = frameSizeItem.Tag?.ToString() ?? string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
job.CustomFrameSize = string.Empty;
|
||||
}
|
||||
else job.CustomFrameSize = string.Empty;
|
||||
|
||||
if (UseCustomOverwriteCheckBox.IsChecked == true && CustomOverwriteComboBox.SelectedItem is ComboBoxItem overwriteItem)
|
||||
{
|
||||
if (Enum.TryParse<OverwriteMode>(overwriteItem.Tag?.ToString(), out var overwriteMode))
|
||||
{
|
||||
job.CustomOverwriteMode = overwriteMode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
job.CustomOverwriteMode = null;
|
||||
}
|
||||
if (UseCustomOverwriteCheckBox.IsChecked == true && CustomOverwriteComboBox.SelectedItem is ComboBoxItem overwriteItem && Enum.TryParse<OverwriteMode>(overwriteItem.Tag?.ToString(), out var overwriteMode))
|
||||
job.CustomOverwriteMode = overwriteMode;
|
||||
else job.CustomOverwriteMode = null;
|
||||
|
||||
if (UseCustomNamingCheckBox.IsChecked == true && CustomNamingComboBox.SelectedItem is ComboBoxItem namingItem)
|
||||
if (UseCustomNamingCheckBox.IsChecked == true && CustomNamingComboBox.SelectedItem is ComboBoxItem namingItem && Enum.TryParse<NamingPattern>(namingItem.Tag?.ToString(), out var namingPattern))
|
||||
{
|
||||
if (Enum.TryParse<NamingPattern>(namingItem.Tag?.ToString(), out var namingPattern))
|
||||
{
|
||||
job.CustomNamingPattern = namingPattern;
|
||||
job.CustomPrefix = string.IsNullOrWhiteSpace(CustomNamingPrefixTextBox.Text) ? "custom" : CustomNamingPrefixTextBox.Text;
|
||||
}
|
||||
job.CustomNamingPattern = namingPattern;
|
||||
job.CustomPrefix = string.IsNullOrWhiteSpace(CustomNamingPrefixTextBox.Text) ? "custom" : CustomNamingPrefixTextBox.Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -227,13 +176,12 @@ namespace Ganimede.Windows
|
||||
job.CustomPrefix = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WpfMessageBox.Show($"Error applying settings: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
WpfMessageBox.Show($"Errore durante l'applicazione delle impostazioni: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="Settings" Height="560" Width="600"
|
||||
Background="#222" WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="20">
|
||||
Title="Impostazioni" Height="600" Width="640"
|
||||
Background="#1E2228" WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="22">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
@@ -14,113 +14,102 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Title -->
|
||||
<TextBlock Grid.Row="0" Text="Settings" FontSize="24" FontWeight="Bold" Foreground="White" Margin="0,0,0,20"/>
|
||||
<TextBlock Grid.Row="0" Text="Impostazioni" FontSize="26" FontWeight="Bold" Foreground="White" Margin="0,0,0,18"/>
|
||||
|
||||
<!-- Settings Content -->
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel>
|
||||
<!-- FFmpeg Settings -->
|
||||
<GroupBox Header="FFmpeg Configuration" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock Text="FFmpeg Binary Folder:" Foreground="#CCC" Margin="0,0,0,5"/>
|
||||
<GroupBox Header="Configurazione FFmpeg" Foreground="White" BorderBrush="#444" Margin="0,0,0,18">
|
||||
<StackPanel Margin="12">
|
||||
<TextBlock Text="Cartella Binari FFmpeg:" Foreground="#CCC" Margin="0,0,0,6"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="FFmpegPathTextBox" Grid.Column="0" Height="30" VerticalContentAlignment="Center"
|
||||
<TextBox x:Name="FFmpegPathTextBox" Grid.Column="0" Height="34" VerticalContentAlignment="Center"
|
||||
Background="#333" Foreground="White" BorderBrush="#555" Margin="0,0,10,0"/>
|
||||
<Button x:Name="BrowseFFmpegButton" Grid.Column="1" Content="Browse" Width="80" Height="30"
|
||||
<Button x:Name="BrowseFFmpegButton" Grid.Column="1" Content="Sfoglia" Width="90" Height="34"
|
||||
Click="BrowseFFmpegButton_Click"/>
|
||||
</Grid>
|
||||
<TextBlock x:Name="FFmpegStatusText" Foreground="#AAA" FontSize="12" Margin="0,5,0,0"/>
|
||||
<TextBlock x:Name="FFmpegStatusText" Foreground="#AAA" FontSize="12" Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Output Settings -->
|
||||
<GroupBox Header="Output Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<CheckBox x:Name="CreateSubfolderCheckBox" Content="Create subfolder for each video"
|
||||
<GroupBox Header="Output" Foreground="White" BorderBrush="#444" Margin="0,0,0,18">
|
||||
<StackPanel Margin="12">
|
||||
<CheckBox x:Name="CreateSubfolderCheckBox" Content="Crea sottocartella per ogni video"
|
||||
Foreground="White" IsChecked="True" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Text="Default Output Folder:" Foreground="#CCC" Margin="0,0,0,5"/>
|
||||
<TextBlock Text="Cartella Output Predefinita:" Foreground="#CCC" Margin="0,0,0,6"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBox x:Name="DefaultOutputTextBox" Grid.Column="0" Height="30" VerticalContentAlignment="Center"
|
||||
<TextBox x:Name="DefaultOutputTextBox" Grid.Column="0" Height="34" VerticalContentAlignment="Center"
|
||||
Background="#333" Foreground="White" BorderBrush="#555" Margin="0,0,10,0"/>
|
||||
<Button x:Name="BrowseOutputButton" Grid.Column="1" Content="Browse" Width="80" Height="30"
|
||||
<Button x:Name="BrowseOutputButton" Grid.Column="1" Content="Sfoglia" Width="90" Height="34"
|
||||
Click="BrowseOutputButton_Click"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Performance Settings -->
|
||||
<GroupBox Header="Default Processing Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock Text="Default Frame Size:" Foreground="#CCC" Margin="0,0,0,5"/>
|
||||
<ComboBox x:Name="FrameSizeComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555">
|
||||
<ComboBoxItem Content="Original Size (Keep aspect ratio)" Tag="original" IsSelected="True" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="320x180 (Fast)" Tag="320,180" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="640x360 (Medium)" Tag="640,360" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="1280x720 (High)" Tag="1280,720" Foreground="Black" Background="White"/>
|
||||
<GroupBox Header="Impostazioni di Elaborazione" Foreground="White" BorderBrush="#444" Margin="0,0,0,18">
|
||||
<StackPanel Margin="12">
|
||||
<TextBlock Text="Dimensione Frame Predefinita:" Foreground="#CCC" Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="FrameSizeComboBox" Height="32" Background="#333" Foreground="White" BorderBrush="#555">
|
||||
<ComboBoxItem Content="Dimensione Originale" Tag="original" IsSelected="True" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="320x180 (Veloce)" Tag="320,180" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="640x360 (Media)" Tag="640,360" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="1280x720 (Alta)" Tag="1280,720" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="1920x1080 (Full HD)" Tag="1920,1080" Foreground="Black" Background="White"/>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Text="Default File Overwrite Behavior:" Foreground="#CCC" Margin="15,15,0,5"/>
|
||||
<ComboBox x:Name="OverwriteModeComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555">
|
||||
<ComboBoxItem Content="Ask each time" Tag="Ask" IsSelected="True" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Skip existing files" Tag="Skip" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Overwrite existing files" Tag="Overwrite" Foreground="Black" Background="White"/>
|
||||
<TextBlock Text="Comportamento Sovrascrittura Predefinito:" Foreground="#CCC" Margin="16,16,0,6"/>
|
||||
<ComboBox x:Name="OverwriteModeComboBox" Height="32" Background="#333" Foreground="White" BorderBrush="#555">
|
||||
<ComboBoxItem Content="Chiedi" Tag="Ask" IsSelected="True" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Salta" Tag="Skip" Foreground="Black" Background="White"/>
|
||||
<ComboBoxItem Content="Sovrascrivi" Tag="Overwrite" Foreground="Black" Background="White"/>
|
||||
</ComboBox>
|
||||
|
||||
<!-- New default extraction mode -->
|
||||
<TextBlock Text="Default Extraction Mode:" Foreground="#CCC" Margin="15,15,0,5"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
|
||||
<RadioButton x:Name="DefaultModeFullRadio" Content="Full" GroupName="DefExtraction" Foreground="White" Margin="0,0,15,0" IsChecked="True"/>
|
||||
<RadioButton x:Name="DefaultModeSingleRadio" Content="Single Frame" GroupName="DefExtraction" Foreground="White" Margin="0,0,15,0"/>
|
||||
<TextBlock Text="Modalita di Estrazione Predefinita:" Foreground="#CCC" Margin="16,16,0,6"/>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<RadioButton x:Name="DefaultModeFullRadio" Content="Completa" GroupName="DefExtraction" Foreground="White" Margin="0,0,18,0" IsChecked="True"/>
|
||||
<RadioButton x:Name="DefaultModeSingleRadio" Content="Singolo Frame" GroupName="DefExtraction" Foreground="White" Margin="0,0,18,0"/>
|
||||
<RadioButton x:Name="DefaultModeAutoRadio" Content="Auto" GroupName="DefExtraction" Foreground="White"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="Auto: analyze video to decide if extracting all frames or only one." Foreground="#777" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,0"/>
|
||||
<TextBlock Text="Auto: analizza il video e decide cosa estrarre." Foreground="#777" FontSize="10" TextWrapping="Wrap" Margin="0,4,0,0"/>
|
||||
|
||||
<TextBlock Text="Single Frame Output (default behaviour = direct in output folder):" Foreground="#CCC" Margin="15,15,0,5"/>
|
||||
<CheckBox x:Name="SingleFrameUseSubfolderCheckBox" Content="Create per-video subfolder for single frame jobs (uncheck = save directly)" Foreground="White" IsChecked="False" ToolTip="Unchecked: single frame saved directly in main output folder. Checked: create video-named subfolder."/>
|
||||
<TextBlock Text="Singolo Frame: salvataggio" Foreground="#CCC" Margin="16,16,0,6"/>
|
||||
<CheckBox x:Name="SingleFrameUseSubfolderCheckBox" Content="Crea sottocartella anche per job a singolo frame" Foreground="White" IsChecked="False"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<!-- File Naming Settings -->
|
||||
<GroupBox Header="File Naming Settings" Foreground="White" BorderBrush="#444" Margin="0,0,0,20">
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock Text="Default Naming Pattern:" Foreground="#CCC" Margin="0,0,0,5"/>
|
||||
<ComboBox x:Name="NamingPatternComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
<GroupBox Header="Naming File" Foreground="White" BorderBrush="#444" Margin="0,0,0,18">
|
||||
<StackPanel Margin="12">
|
||||
<TextBlock Text="Pattern di Naming Predefinito:" Foreground="#CCC" Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="NamingPatternComboBox" Height="32" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
SelectionChanged="NamingPatternComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="VideoName + Progressive (recommended)" Tag="VideoNameProgressive" IsSelected="True"/>
|
||||
<ComboBoxItem Content="Frame + Progressive" Tag="FrameProgressive"/>
|
||||
<ComboBoxItem Content="VideoName + Timestamp" Tag="VideoNameTimestamp"/>
|
||||
<ComboBoxItem Content="Custom Prefix + Progressive" Tag="CustomProgressive"/>
|
||||
<ComboBoxItem Content="Timestamp Only" Tag="TimestampOnly"/>
|
||||
<ComboBoxItem Content="VideoName + Frame + Progressive" Tag="VideoNameFrameProgressive"/>
|
||||
<ComboBoxItem Content="NomeVideo + Progressivo" Tag="VideoNameProgressive" IsSelected="True"/>
|
||||
<ComboBoxItem Content="Frame + Progressivo" Tag="FrameProgressive"/>
|
||||
<ComboBoxItem Content="NomeVideo + Timestamp" Tag="VideoNameTimestamp"/>
|
||||
<ComboBoxItem Content="Prefisso Custom + Progressivo" Tag="CustomProgressive"/>
|
||||
<ComboBoxItem Content="Solo Timestamp" Tag="TimestampOnly"/>
|
||||
<ComboBoxItem Content="NomeVideo + Frame + Progressivo" Tag="VideoNameFrameProgressive"/>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Text="Default Custom Prefix:" Foreground="#CCC" Margin="0,10,0,5"/>
|
||||
<TextBox x:Name="CustomPrefixTextBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
<TextBlock Text="Prefisso Custom Predefinito:" Foreground="#CCC" Margin="0,12,0,6"/>
|
||||
<TextBox x:Name="CustomPrefixTextBox" Height="32" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
VerticalContentAlignment="Center" Text="custom" TextChanged="CustomPrefixTextBox_TextChanged"/>
|
||||
|
||||
<TextBlock Text="Preview:" Foreground="#CCC" Margin="0,10,0,2" FontSize="11"/>
|
||||
<TextBlock x:Name="NamingPreviewText" Text="VID20250725_000001.png" Foreground="#4FC3F7" FontSize="11"
|
||||
FontFamily="Consolas" Background="#1A1A1A" Padding="5" Margin="0,0,0,5"/>
|
||||
|
||||
<TextBlock Text="Available patterns:" Foreground="#777" FontSize="10" Margin="0,5,0,2"/>
|
||||
<StackPanel Margin="0,0,0,0">
|
||||
<TextBlock Text="VideoName + Progressive: Best for organization" Foreground="#777" FontSize="10"/>
|
||||
<TextBlock Text="Frame + Progressive: Classic naming (frame_000001)" Foreground="#777" FontSize="10"/>
|
||||
<TextBlock Text="VideoName + Timestamp: Based on video time" Foreground="#777" FontSize="10"/>
|
||||
<TextBlock Text="Custom Prefix: Use your own prefix" Foreground="#777" FontSize="10"/>
|
||||
<TextBlock Text="Timestamp Only: Human readable time" Foreground="#777" FontSize="10"/>
|
||||
<TextBlock Text="VideoName + Frame: Combined format" Foreground="#777" FontSize="10"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="Anteprima:" Foreground="#CCC" Margin="0,12,0,4" FontSize="11"/>
|
||||
<TextBlock x:Name="NamingPreviewText" Text="Video1_000001.png" Foreground="#4FC3F7" FontSize="11"
|
||||
FontFamily="Consolas" Background="#1A1A1A" Padding="6" Margin="0,0,0,6"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
@@ -128,9 +117,9 @@
|
||||
|
||||
<!-- Buttons -->
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,20,0,0">
|
||||
<Button x:Name="SaveButton" Content="Save" Width="80" Height="35" Margin="0,0,10,0"
|
||||
<Button x:Name="SaveButton" Content="Salva" Width="95" Height="40" Margin="0,0,12,0"
|
||||
Click="SaveButton_Click" Background="#4FC3F7" Foreground="White" BorderThickness="0"/>
|
||||
<Button x:Name="CancelButton" Content="Cancel" Width="80" Height="35"
|
||||
<Button x:Name="CancelButton" Content="Annulla" Width="100" Height="40"
|
||||
Click="CancelButton_Click"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
@@ -24,9 +24,8 @@ namespace Ganimede.Windows
|
||||
DefaultOutputTextBox.Text = Settings.Default.LastOutputFolder;
|
||||
CreateSubfolderCheckBox.IsChecked = Settings.Default.CreateSubfolder;
|
||||
var singleFrameChk = GetCheckBox("SingleFrameUseSubfolderCheckBox");
|
||||
if (singleFrameChk != null)
|
||||
singleFrameChk.IsChecked = Settings.Default.SingleFrameUseSubfolder;
|
||||
|
||||
if (singleFrameChk != null) singleFrameChk.IsChecked = Settings.Default.SingleFrameUseSubfolder;
|
||||
|
||||
var frameSize = Settings.Default.FrameSize;
|
||||
foreach (System.Windows.Controls.ComboBoxItem item in FrameSizeComboBox.Items)
|
||||
{
|
||||
@@ -47,15 +46,14 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
// Default extraction mode
|
||||
switch (Settings.Default.DefaultExtractionMode)
|
||||
{
|
||||
case "SingleFrame":
|
||||
if (GetDefaultModeRadio("DefaultModeSingleRadio") is { } r1) r1.IsChecked = true; break;
|
||||
GetDefaultModeRadio("DefaultModeSingleRadio")!.IsChecked = true; break;
|
||||
case "Auto":
|
||||
if (GetDefaultModeRadio("DefaultModeAutoRadio") is { } r2) r2.IsChecked = true; break;
|
||||
GetDefaultModeRadio("DefaultModeAutoRadio")!.IsChecked = true; break;
|
||||
default:
|
||||
if (GetDefaultModeRadio("DefaultModeFullRadio") is { } r3) r3.IsChecked = true; break;
|
||||
GetDefaultModeRadio("DefaultModeFullRadio")!.IsChecked = true; break;
|
||||
}
|
||||
|
||||
UpdateFFmpegStatus();
|
||||
@@ -68,53 +66,38 @@ namespace Ganimede.Windows
|
||||
return "Full";
|
||||
}
|
||||
|
||||
private bool GetSingleFrameUseSubfolder()
|
||||
{
|
||||
return GetCheckBox("SingleFrameUseSubfolderCheckBox")?.IsChecked == true;
|
||||
}
|
||||
private bool GetSingleFrameUseSubfolder() => GetCheckBox("SingleFrameUseSubfolderCheckBox")?.IsChecked == true;
|
||||
|
||||
private void UpdateFFmpegStatus()
|
||||
{
|
||||
var path = FFmpegPathTextBox.Text;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
FFmpegStatusText.Text = "No path specified";
|
||||
FFmpegStatusText.Text = "Nessun percorso specificato";
|
||||
FFmpegStatusText.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Orange);
|
||||
}
|
||||
else if (ValidateFFMpegPath(path))
|
||||
{
|
||||
FFmpegStatusText.Text = "? Valid FFmpeg installation found";
|
||||
FFmpegStatusText.Text = "? FFmpeg valido";
|
||||
FFmpegStatusText.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.LightGreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
FFmpegStatusText.Text = "? FFmpeg binaries not found in specified path";
|
||||
FFmpegStatusText.Text = "? Binari FFmpeg non trovati";
|
||||
FFmpegStatusText.Foreground = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateFFMpegPath(string path)
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
return false;
|
||||
|
||||
var ffmpegPath = Path.Combine(path, "ffmpeg.exe");
|
||||
var ffprobePath = Path.Combine(path, "ffprobe.exe");
|
||||
|
||||
return File.Exists(ffmpegPath) && File.Exists(ffprobePath);
|
||||
if (!Directory.Exists(path)) return false;
|
||||
return File.Exists(Path.Combine(path, "ffmpeg.exe")) && File.Exists(Path.Combine(path, "ffprobe.exe"));
|
||||
}
|
||||
|
||||
private void BrowseFFmpegButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||
{
|
||||
Description = "Select FFmpeg binary folder",
|
||||
ShowNewFolderButton = false
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(FFmpegPathTextBox.Text))
|
||||
dialog.SelectedPath = FFmpegPathTextBox.Text;
|
||||
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog { Description = "Seleziona cartella binari FFmpeg", ShowNewFolderButton = false };
|
||||
if (!string.IsNullOrEmpty(FFmpegPathTextBox.Text)) dialog.SelectedPath = FFmpegPathTextBox.Text;
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
FFmpegPathTextBox.Text = dialog.SelectedPath;
|
||||
@@ -124,19 +107,9 @@ namespace Ganimede.Windows
|
||||
|
||||
private void BrowseOutputButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||
{
|
||||
Description = "Select default output folder",
|
||||
ShowNewFolderButton = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(DefaultOutputTextBox.Text))
|
||||
dialog.SelectedPath = DefaultOutputTextBox.Text;
|
||||
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||
{
|
||||
DefaultOutputTextBox.Text = dialog.SelectedPath;
|
||||
}
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog { Description = "Seleziona cartella output predefinita", ShowNewFolderButton = true };
|
||||
if (!string.IsNullOrEmpty(DefaultOutputTextBox.Text)) dialog.SelectedPath = DefaultOutputTextBox.Text;
|
||||
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) DefaultOutputTextBox.Text = dialog.SelectedPath;
|
||||
}
|
||||
|
||||
private void NamingPatternComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) { }
|
||||
@@ -156,13 +129,13 @@ namespace Ganimede.Windows
|
||||
Settings.Default.DefaultExtractionMode = GetSelectedDefaultExtractionMode();
|
||||
Settings.Default.SingleFrameUseSubfolder = GetSingleFrameUseSubfolder();
|
||||
Settings.Default.Save();
|
||||
Debug.WriteLine("[SETTINGS] Settings saved successfully");
|
||||
Debug.WriteLine("[SETTINGS] Salvate");
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WpfMessageBox.Show($"Error saving settings: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
WpfMessageBox.Show($"Errore durante il salvataggio: {ex.Message}", "Errore", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
Debug.WriteLine($"[ERROR] Failed to save settings: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user