Aggiunta gestione modelli di denominazione file video
- Modificato `MainWindow.xaml` per includere il binding del modello di denominazione. - Introdotta l'enumerazione `NamingPattern` in `VideoJob.cs`. - Aggiunti campi e proprietà per gestire il modello di denominazione e il prefisso personalizzato. - Aggiornate le impostazioni predefinite per `FrameSize` e aggiunte nuove impostazioni per `DefaultNamingPattern` e `DefaultCustomPrefix`. - Aggiornato `VideoProcessingService.cs` per utilizzare il modello di denominazione e il prefisso personalizzato. - Modificato `JobConfigWindow.xaml` per aggiungere controlli per le impostazioni di denominazione. - Aggiunti controlli per le impostazioni di denominazione predefinite in `SettingsWindow.xaml`. - Creata la classe `NamingHelper` per la generazione dei nomi dei file.
This commit is contained in:
62
Ganimede/Ganimede/Helpers/NamingHelper.cs
Normal file
62
Ganimede/Ganimede/Helpers/NamingHelper.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
using Ganimede.Models;
|
||||
|
||||
namespace Ganimede.Helpers
|
||||
{
|
||||
public static class NamingHelper
|
||||
{
|
||||
public static string GenerateFileName(NamingPattern pattern, VideoJob job, int frameIndex, TimeSpan frameTime, string customPrefix = "")
|
||||
{
|
||||
var videoName = Path.GetFileNameWithoutExtension(job.VideoPath);
|
||||
var progressiveNumber = frameIndex + 1; // Start from 1 instead of 0
|
||||
|
||||
return pattern switch
|
||||
{
|
||||
NamingPattern.VideoNameProgressive =>
|
||||
$"{videoName}_{progressiveNumber:D6}.png",
|
||||
|
||||
NamingPattern.FrameProgressive =>
|
||||
$"frame_{progressiveNumber:D6}.png",
|
||||
|
||||
NamingPattern.VideoNameTimestamp =>
|
||||
$"{videoName}_{(int)frameTime.TotalMilliseconds:D6}ms.png",
|
||||
|
||||
NamingPattern.CustomProgressive =>
|
||||
$"{(string.IsNullOrEmpty(customPrefix) ? "custom" : customPrefix)}_{progressiveNumber:D6}.png",
|
||||
|
||||
NamingPattern.TimestampOnly =>
|
||||
$"{(int)frameTime.Hours:D2}h{frameTime.Minutes:D2}m{frameTime.Seconds:D2}s{frameTime.Milliseconds:D3}ms.png",
|
||||
|
||||
NamingPattern.VideoNameFrameProgressive =>
|
||||
$"{videoName}_frame_{progressiveNumber:D6}.png",
|
||||
|
||||
_ => $"frame_{progressiveNumber:D6}.png"
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetPatternDescription(NamingPattern pattern)
|
||||
{
|
||||
var field = pattern.GetType().GetField(pattern.ToString());
|
||||
var attribute = field?.GetCustomAttribute<DescriptionAttribute>();
|
||||
return attribute?.Description ?? pattern.ToString();
|
||||
}
|
||||
|
||||
public static string GetPatternExample(NamingPattern pattern, string videoName = "VID20250725", string customPrefix = "custom")
|
||||
{
|
||||
var job = new VideoJob
|
||||
{
|
||||
VideoPath = $"{videoName}.mp4",
|
||||
OutputFolder = "",
|
||||
CustomPrefix = customPrefix
|
||||
};
|
||||
|
||||
var frameTime = new TimeSpan(0, 12, 34, 567); // 12:34.567
|
||||
var example = GenerateFileName(pattern, job, 0, frameTime, customPrefix); // frameIndex 0 will become 1
|
||||
|
||||
return example;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,10 +141,11 @@
|
||||
<!-- Job specific settings display -->
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" FontSize="8" Foreground="#666" Margin="0,2,0,0">
|
||||
<TextBlock.Text>
|
||||
<MultiBinding StringFormat="{}📁 {0} | 📐 {1} | 🔄 {2}">
|
||||
<MultiBinding StringFormat="{}📁 {0} | 📐 {1} | 🔄 {2} | 🏷 {3}">
|
||||
<Binding Path="OutputFolderDisplay"/>
|
||||
<Binding Path="FrameSizeDisplay"/>
|
||||
<Binding Path="OverwriteModeDisplay"/>
|
||||
<Binding Path="NamingPatternDisplay"/>
|
||||
</MultiBinding>
|
||||
</TextBlock.Text>
|
||||
</TextBlock>
|
||||
|
||||
@@ -19,6 +19,27 @@ namespace Ganimede.Models
|
||||
Overwrite
|
||||
}
|
||||
|
||||
public enum NamingPattern
|
||||
{
|
||||
[Description("VideoName + Progressive (VID20250725_000001)")]
|
||||
VideoNameProgressive,
|
||||
|
||||
[Description("Frame + Progressive (frame_000001)")]
|
||||
FrameProgressive,
|
||||
|
||||
[Description("VideoName + Timestamp (VID20250725_001234ms)")]
|
||||
VideoNameTimestamp,
|
||||
|
||||
[Description("Custom Prefix + Progressive (custom_000001)")]
|
||||
CustomProgressive,
|
||||
|
||||
[Description("Timestamp Only (00h12m34s567ms)")]
|
||||
TimestampOnly,
|
||||
|
||||
[Description("VideoName + Frame + Progressive (VID20250725_frame_000001)")]
|
||||
VideoNameFrameProgressive
|
||||
}
|
||||
|
||||
public class VideoJob : INotifyPropertyChanged
|
||||
{
|
||||
private JobStatus _status = JobStatus.Pending;
|
||||
@@ -28,6 +49,8 @@ namespace Ganimede.Models
|
||||
private string _customFrameSize = string.Empty;
|
||||
private OverwriteMode? _customOverwriteMode = null;
|
||||
private bool _customCreateSubfolder = true;
|
||||
private NamingPattern? _customNamingPattern = null;
|
||||
private string _customPrefix = string.Empty;
|
||||
|
||||
public required string VideoPath { get; set; }
|
||||
public string VideoName => System.IO.Path.GetFileNameWithoutExtension(VideoPath);
|
||||
@@ -109,16 +132,43 @@ namespace Ganimede.Models
|
||||
}
|
||||
}
|
||||
|
||||
public NamingPattern? CustomNamingPattern
|
||||
{
|
||||
get => _customNamingPattern;
|
||||
set
|
||||
{
|
||||
_customNamingPattern = value;
|
||||
OnPropertyChanged(nameof(CustomNamingPattern));
|
||||
OnPropertyChanged(nameof(NamingPatternDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
public string CustomPrefix
|
||||
{
|
||||
get => _customPrefix;
|
||||
set
|
||||
{
|
||||
_customPrefix = value;
|
||||
OnPropertyChanged(nameof(CustomPrefix));
|
||||
OnPropertyChanged(nameof(NamingPatternDisplay));
|
||||
}
|
||||
}
|
||||
|
||||
// Display properties for UI
|
||||
public string OutputFolderDisplay =>
|
||||
string.IsNullOrEmpty(CustomOutputFolder) ? "Default" : System.IO.Path.GetFileName(CustomOutputFolder) + (CustomCreateSubfolder ? "/??" : "");
|
||||
|
||||
public string FrameSizeDisplay =>
|
||||
string.IsNullOrEmpty(CustomFrameSize) ? "Default" : CustomFrameSize;
|
||||
string.IsNullOrEmpty(CustomFrameSize) ? "Default" :
|
||||
(CustomFrameSize == "original" ? "Original" : CustomFrameSize);
|
||||
|
||||
public string OverwriteModeDisplay =>
|
||||
CustomOverwriteMode?.ToString() ?? "Default";
|
||||
|
||||
public string NamingPatternDisplay =>
|
||||
CustomNamingPattern?.ToString() ?? "Default" +
|
||||
(!string.IsNullOrEmpty(CustomPrefix) ? $" ({CustomPrefix}_)" : "");
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
|
||||
26
Ganimede/Ganimede/Properties/Settings.Designer.cs
generated
26
Ganimede/Ganimede/Properties/Settings.Designer.cs
generated
@@ -73,7 +73,7 @@ namespace Ganimede.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("320,180")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("original")]
|
||||
public string FrameSize {
|
||||
get {
|
||||
return ((string)(this["FrameSize"]));
|
||||
@@ -94,5 +94,29 @@ namespace Ganimede.Properties {
|
||||
this["DefaultOverwriteMode"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("VideoNameProgressive")]
|
||||
public string DefaultNamingPattern {
|
||||
get {
|
||||
return ((string)(this["DefaultNamingPattern"]));
|
||||
}
|
||||
set {
|
||||
this["DefaultNamingPattern"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("custom")]
|
||||
public string DefaultCustomPrefix {
|
||||
get {
|
||||
return ((string)(this["DefaultCustomPrefix"]));
|
||||
}
|
||||
set {
|
||||
this["DefaultCustomPrefix"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,16 @@
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="FrameSize" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">320,180</Value>
|
||||
<Value Profile="(Default)">original</Value>
|
||||
</Setting>
|
||||
<Setting Name="DefaultOverwriteMode" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">Ask</Value>
|
||||
</Setting>
|
||||
<Setting Name="DefaultNamingPattern" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">VideoNameProgressive</Value>
|
||||
</Setting>
|
||||
<Setting Name="DefaultCustomPrefix" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">custom</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -7,6 +7,7 @@ using FFMpegCore;
|
||||
using System.IO;
|
||||
using Ganimede.Models;
|
||||
using Ganimede.Properties;
|
||||
using Ganimede.Helpers;
|
||||
|
||||
namespace Ganimede.Services
|
||||
{
|
||||
@@ -144,9 +145,11 @@ namespace Ganimede.Services
|
||||
|
||||
var frameSize = GetFrameSize(job);
|
||||
var overwriteMode = GetOverwriteMode(job);
|
||||
var namingPattern = GetNamingPattern(job);
|
||||
var customPrefix = GetCustomPrefix(job);
|
||||
|
||||
// Check for existing files if needed
|
||||
var existingFiles = Directory.GetFiles(job.OutputFolder, "frame_*.png");
|
||||
// Check for existing files if needed (using naming pattern)
|
||||
var existingFiles = Directory.GetFiles(job.OutputFolder, "*.png");
|
||||
if (existingFiles.Length > 0 && overwriteMode == OverwriteMode.Ask)
|
||||
{
|
||||
var dialogResult = System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||
@@ -176,7 +179,9 @@ namespace Ganimede.Services
|
||||
return;
|
||||
}
|
||||
|
||||
string framePath = Path.Combine(job.OutputFolder, $"frame_{i:D6}.png");
|
||||
var frameTime = TimeSpan.FromSeconds((double)i / frameRate);
|
||||
var fileName = NamingHelper.GenerateFileName(namingPattern, job, i, frameTime, customPrefix);
|
||||
string framePath = Path.Combine(job.OutputFolder, fileName);
|
||||
|
||||
// Check if file exists and handle according to overwrite mode
|
||||
if (File.Exists(framePath) && overwriteMode == OverwriteMode.Skip)
|
||||
@@ -228,14 +233,14 @@ namespace Ganimede.Services
|
||||
// Use job-specific frame size if set, otherwise use default setting
|
||||
var frameSize = !string.IsNullOrEmpty(job.CustomFrameSize) ? job.CustomFrameSize : Settings.Default.FrameSize;
|
||||
|
||||
if (string.IsNullOrEmpty(frameSize))
|
||||
return (320, 180);
|
||||
if (string.IsNullOrEmpty(frameSize) || frameSize == "original")
|
||||
return (-1, -1); // Special value indicating original size
|
||||
|
||||
var parts = frameSize.Split(',');
|
||||
if (parts.Length == 2 && int.TryParse(parts[0], out int width) && int.TryParse(parts[1], out int height))
|
||||
return (width, height);
|
||||
|
||||
return (320, 180);
|
||||
return (-1, -1); // Default to original size if parsing fails
|
||||
}
|
||||
|
||||
private OverwriteMode GetOverwriteMode(VideoJob job)
|
||||
@@ -251,27 +256,77 @@ namespace Ganimede.Services
|
||||
return OverwriteMode.Ask;
|
||||
}
|
||||
|
||||
private NamingPattern GetNamingPattern(VideoJob job)
|
||||
{
|
||||
// Use job-specific naming pattern if set, otherwise use default setting
|
||||
if (job.CustomNamingPattern.HasValue)
|
||||
return job.CustomNamingPattern.Value;
|
||||
|
||||
var defaultPattern = Settings.Default.DefaultNamingPattern;
|
||||
if (Enum.TryParse<NamingPattern>(defaultPattern, out var pattern))
|
||||
return pattern;
|
||||
|
||||
return NamingPattern.VideoNameProgressive;
|
||||
}
|
||||
|
||||
private string GetCustomPrefix(VideoJob job)
|
||||
{
|
||||
// Use job-specific custom prefix if set, otherwise use default setting
|
||||
if (!string.IsNullOrEmpty(job.CustomPrefix))
|
||||
return job.CustomPrefix;
|
||||
|
||||
return Settings.Default.DefaultCustomPrefix ?? "custom";
|
||||
}
|
||||
|
||||
private async Task ExtractFrameAsync(VideoJob job, int frameIndex, int frameRate, (int width, int height) frameSize, string framePath)
|
||||
{
|
||||
var frameTime = TimeSpan.FromSeconds((double)frameIndex / frameRate);
|
||||
|
||||
try
|
||||
{
|
||||
// Try with PNG codec first
|
||||
await FFMpegArguments
|
||||
.FromFileInput(job.VideoPath)
|
||||
.OutputToFile(framePath, true, options => options
|
||||
.Seek(frameTime)
|
||||
.WithFrameOutputCount(1)
|
||||
.WithVideoCodec("png")
|
||||
.Resize(frameSize.width, frameSize.height))
|
||||
.ProcessAsynchronously();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback without codec specification
|
||||
// Check if we should use original size
|
||||
if (frameSize.width == -1 && frameSize.height == -1)
|
||||
{
|
||||
// Extract frame with original video size (no resize)
|
||||
try
|
||||
{
|
||||
await FFMpegArguments
|
||||
.FromFileInput(job.VideoPath)
|
||||
.OutputToFile(framePath, true, options => options
|
||||
.Seek(frameTime)
|
||||
.WithFrameOutputCount(1)
|
||||
.WithVideoCodec("png"))
|
||||
.ProcessAsynchronously();
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback without codec specification
|
||||
await FFMpegArguments
|
||||
.FromFileInput(job.VideoPath)
|
||||
.OutputToFile(framePath, true, options => options
|
||||
.Seek(frameTime)
|
||||
.WithFrameOutputCount(1))
|
||||
.ProcessAsynchronously();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract frame with specified resize
|
||||
try
|
||||
{
|
||||
await FFMpegArguments
|
||||
.FromFileInput(job.VideoPath)
|
||||
.OutputToFile(framePath, true, options => options
|
||||
.Seek(frameTime)
|
||||
.WithFrameOutputCount(1)
|
||||
.WithVideoCodec("png")
|
||||
.Resize(frameSize.width, frameSize.height))
|
||||
.ProcessAsynchronously();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Fallback without codec specification
|
||||
await FFMpegArguments
|
||||
.FromFileInput(job.VideoPath)
|
||||
.OutputToFile(framePath, true, options => options
|
||||
@@ -280,11 +335,11 @@ namespace Ganimede.Services
|
||||
.Resize(frameSize.width, frameSize.height))
|
||||
.ProcessAsynchronously();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[ERROR] Failed to extract frame {frameIndex} from {job.VideoName}: {ex.Message}");
|
||||
// Continue processing other frames even if this one fails
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"[ERROR] Failed to extract frame {frameIndex} from {job.VideoName}: {ex.Message}");
|
||||
// Continue processing other frames even if this one fails
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,10 +52,11 @@
|
||||
|
||||
<ComboBox x:Name="CustomFrameSizeComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomFrameSizeCheckBox}">
|
||||
<ComboBoxItem Content="320x180 (Fast)" Tag="320,180"/>
|
||||
<ComboBoxItem Content="640x360 (Medium)" Tag="640,360"/>
|
||||
<ComboBoxItem Content="1280x720 (High)" Tag="1280,720"/>
|
||||
<ComboBoxItem Content="1920x1080 (Full HD)" Tag="1920,1080"/>
|
||||
<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"/>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -68,12 +69,44 @@
|
||||
|
||||
<ComboBox x:Name="CustomOverwriteComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555"
|
||||
IsEnabled="{Binding IsChecked, ElementName=UseCustomOverwriteCheckBox}">
|
||||
<ComboBoxItem Content="Ask each time" Tag="Ask"/>
|
||||
<ComboBoxItem Content="Skip existing files" Tag="Skip"/>
|
||||
<ComboBoxItem Content="Overwrite existing files" Tag="Overwrite"/>
|
||||
<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"/>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using Ganimede.Models;
|
||||
using Ganimede.Helpers;
|
||||
using WpfMessageBox = System.Windows.MessageBox;
|
||||
|
||||
namespace Ganimede.Windows
|
||||
@@ -62,6 +63,21 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Naming settings
|
||||
if (firstJob.CustomNamingPattern.HasValue)
|
||||
{
|
||||
UseCustomNamingCheckBox.IsChecked = true;
|
||||
foreach (ComboBoxItem item in CustomNamingComboBox.Items)
|
||||
{
|
||||
if (item.Tag?.ToString() == firstJob.CustomNamingPattern.Value.ToString())
|
||||
{
|
||||
CustomNamingComboBox.SelectedItem = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CustomNamingPrefixTextBox.Text = firstJob.CustomPrefix ?? "custom";
|
||||
}
|
||||
}
|
||||
|
||||
// Set default selections if nothing is selected
|
||||
@@ -70,6 +86,35 @@ namespace Ganimede.Windows
|
||||
|
||||
if (CustomOverwriteComboBox.SelectedItem == null)
|
||||
CustomOverwriteComboBox.SelectedIndex = 0;
|
||||
|
||||
if (CustomNamingComboBox.SelectedItem == null)
|
||||
CustomNamingComboBox.SelectedIndex = 0;
|
||||
|
||||
UpdateJobNamingPreview();
|
||||
}
|
||||
|
||||
private void UpdateJobNamingPreview()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (UseCustomNamingCheckBox.IsChecked == true &&
|
||||
CustomNamingComboBox.SelectedItem is ComboBoxItem selectedItem &&
|
||||
Enum.TryParse<NamingPattern>(selectedItem.Tag?.ToString(), out var pattern))
|
||||
{
|
||||
var firstVideoName = _selectedJobs.FirstOrDefault()?.VideoName ?? "Video1";
|
||||
var customPrefix = string.IsNullOrWhiteSpace(CustomNamingPrefixTextBox.Text) ? "custom" : CustomNamingPrefixTextBox.Text;
|
||||
var example = NamingHelper.GetPatternExample(pattern, firstVideoName, customPrefix);
|
||||
JobNamingPreviewText.Text = example;
|
||||
}
|
||||
else
|
||||
{
|
||||
JobNamingPreviewText.Text = "Video1_000001.png (using default)";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
JobNamingPreviewText.Text = "Video1_000001.png";
|
||||
}
|
||||
}
|
||||
|
||||
private void UseCustomOutputCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
|
||||
@@ -87,6 +132,21 @@ namespace Ganimede.Windows
|
||||
// Enable/disable controls handled by binding
|
||||
}
|
||||
|
||||
private void UseCustomNamingCheckBox_CheckedChanged(object sender, RoutedEventArgs e)
|
||||
{
|
||||
UpdateJobNamingPreview();
|
||||
}
|
||||
|
||||
private void CustomNamingComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
UpdateJobNamingPreview();
|
||||
}
|
||||
|
||||
private void CustomNamingPrefixTextBox_TextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
UpdateJobNamingPreview();
|
||||
}
|
||||
|
||||
private void BrowseCustomOutputButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
using var dialog = new System.Windows.Forms.FolderBrowserDialog
|
||||
@@ -149,6 +209,21 @@ namespace Ganimede.Windows
|
||||
{
|
||||
job.CustomOverwriteMode = null;
|
||||
}
|
||||
|
||||
// Apply naming settings
|
||||
if (UseCustomNamingCheckBox.IsChecked == true && CustomNamingComboBox.SelectedItem is ComboBoxItem namingItem)
|
||||
{
|
||||
if (Enum.TryParse<NamingPattern>(namingItem.Tag?.ToString(), out var namingPattern))
|
||||
{
|
||||
job.CustomNamingPattern = namingPattern;
|
||||
job.CustomPrefix = string.IsNullOrWhiteSpace(CustomNamingPrefixTextBox.Text) ? "custom" : CustomNamingPrefixTextBox.Text;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
job.CustomNamingPattern = null;
|
||||
job.CustomPrefix = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
DialogResult = true;
|
||||
|
||||
@@ -62,20 +62,55 @@
|
||||
<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="320x180 (Fast)" Tag="320,180" IsSelected="True"/>
|
||||
<ComboBoxItem Content="640x360 (Medium)" Tag="640,360"/>
|
||||
<ComboBoxItem Content="1280x720 (High)" Tag="1280,720"/>
|
||||
<ComboBoxItem Content="1920x1080 (Full HD)" Tag="1920,1080"/>
|
||||
<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"/>
|
||||
</ComboBox>
|
||||
|
||||
<TextBlock Text="Default File Overwrite Behavior:" Foreground="#CCC" Margin="0,15,0,5"/>
|
||||
<ComboBox x:Name="OverwriteModeComboBox" Height="30" Background="#333" Foreground="White" BorderBrush="#555">
|
||||
<ComboBoxItem Content="Ask each time" Tag="Ask" IsSelected="True"/>
|
||||
<ComboBoxItem Content="Skip existing files" Tag="Skip"/>
|
||||
<ComboBoxItem Content="Overwrite existing files" Tag="Overwrite"/>
|
||||
<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"/>
|
||||
</ComboBox>
|
||||
</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"
|
||||
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"/>
|
||||
</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"
|
||||
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>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Load naming pattern settings when controls are generated
|
||||
// var namingPattern = Settings.Default.DefaultNamingPattern;
|
||||
// CustomPrefixTextBox.Text = Settings.Default.DefaultCustomPrefix;
|
||||
|
||||
UpdateFFmpegStatus();
|
||||
}
|
||||
|
||||
@@ -111,6 +115,17 @@ namespace Ganimede.Windows
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement naming pattern event handlers when controls are generated
|
||||
private void NamingPatternComboBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||
{
|
||||
// UpdateNamingPreview();
|
||||
}
|
||||
|
||||
private void CustomPrefixTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
|
||||
{
|
||||
// UpdateNamingPreview();
|
||||
}
|
||||
|
||||
private void SaveButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
@@ -125,6 +140,10 @@ namespace Ganimede.Windows
|
||||
|
||||
var selectedOverwriteItem = OverwriteModeComboBox.SelectedItem as System.Windows.Controls.ComboBoxItem;
|
||||
Settings.Default.DefaultOverwriteMode = selectedOverwriteItem?.Tag?.ToString() ?? "Ask";
|
||||
|
||||
// TODO: Save naming settings when controls are available
|
||||
// Settings.Default.DefaultNamingPattern = ...
|
||||
// Settings.Default.DefaultCustomPrefix = ...
|
||||
|
||||
Settings.Default.Save();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user