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:
Alberto Balbo
2025-09-07 22:42:36 +02:00
parent 91695f350c
commit bf436d0926
10 changed files with 402 additions and 42 deletions

View 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;
}
}
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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;
}
}
}
}

View File

@@ -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>

View File

@@ -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
}
}
}

View File

@@ -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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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();