using Avalonia.Controls; using Avalonia.Interactivity; using Avalonia.Platform.Storage; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; using System.Diagnostics; using System.IO; using System.Text; using System.Threading.Tasks; namespace ImageCatalog_2.AvaloniaViews; public partial class FaceAiTabView : Avalonia.Controls.UserControl { private readonly ILogger _logger; public FaceAiTabView() { InitializeComponent(); _logger = Program.ServiceProvider.GetService(typeof(ILogger)) as ILogger ?? NullLogger.Instance; } private async void SelectFaceExecutable_Click(object? sender, RoutedEventArgs e) { var executableBox = this.FindControl("FaceExecutablePathTextBox"); if (executableBox is null) { return; } var topLevel = TopLevel.GetTopLevel(this); var storageProvider = topLevel?.StorageProvider; if (storageProvider is null) { return; } var files = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions { Title = "Seleziona face_encoder.exe", FileTypeFilter = [ new FilePickerFileType("Eseguibile") { Patterns = ["*.exe"] }, new FilePickerFileType("Tutti i file") { Patterns = ["*.*"] } ] }); if (files.Count > 0) { executableBox.Text = files[0].Path.LocalPath; if (DataContext is DataModel model) { model.FaceExecutablePath = executableBox.Text; } } } private async void SelectFaceOutputFile_Click(object? sender, RoutedEventArgs e) { var outputBox = this.FindControl("FaceOutputFolderTextBox"); if (outputBox is null) { return; } var topLevel = TopLevel.GetTopLevel(this); var storageProvider = topLevel?.StorageProvider; if (storageProvider is null) { return; } var files = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions { Title = "Seleziona file output encodings (.pkl)", SuggestedFileName = "encodings.pkl", DefaultExtension = "pkl", FileTypeChoices = [ new FilePickerFileType("Pickle file") { Patterns = ["*.pkl"] } ], ShowOverwritePrompt = true }); if (files is not null) { outputBox.Text = files.Path.LocalPath; if (DataContext is DataModel model) { model.FaceOutputFolderPath = outputBox.Text; } } } private void OpenFaceExecutableFolder_Click(object? sender, RoutedEventArgs e) { var executableBox = this.FindControl("FaceExecutablePathTextBox"); if (executableBox is null) { return; } var path = executableBox.Text?.Trim(); if (string.IsNullOrWhiteSpace(path)) { return; } if (File.Exists(path)) { OpenInExplorer(path); return; } var directory = Path.GetDirectoryName(path); OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? path : directory); } private void OpenFaceOutputFolder_Click(object? sender, RoutedEventArgs e) { var outputBox = this.FindControl("FaceOutputFolderTextBox"); if (outputBox is null) { return; } var outputPath = outputBox.Text?.Trim(); if (string.IsNullOrWhiteSpace(outputPath)) { return; } if (File.Exists(outputPath)) { OpenInExplorer(outputPath); return; } var directory = Path.GetDirectoryName(outputPath); OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? outputPath : directory); } private void OpenFaceDestinationFolder_Click(object? sender, RoutedEventArgs e) { var destBox = this.FindControl("FaceDestinationPathTextBox"); string? path = destBox?.Text?.Trim(); if (string.IsNullOrWhiteSpace(path) && DataContext is DataModel model) { path = (model.DestinationPath ?? string.Empty).Trim(); } if (string.IsNullOrWhiteSpace(path)) { return; } var directory = Path.GetDirectoryName(path); OpenInExplorer(string.IsNullOrWhiteSpace(directory) ? path : directory); } private async void RunFaceEncoder_Click(object? sender, RoutedEventArgs e) { var executableBox = this.FindControl("FaceExecutablePathTextBox"); var outputFolderBox = this.FindControl("FaceOutputFolderTextBox"); var outputLogBox = this.FindControl("FaceOutputTextBox"); var statusBlock = this.FindControl("FaceStatusTextBlock"); var runButton = this.FindControl("FaceRunButton"); if (executableBox is null || outputFolderBox is null || outputLogBox is null || statusBlock is null || runButton is null) { return; } if (DataContext is not DataModel model) { statusBlock.Text = "DataContext non valido."; return; } var executablePath = executableBox.Text?.Trim().Trim('"') ?? string.Empty; var outputFilePath = outputFolderBox.Text?.Trim().Trim('"') ?? string.Empty; var imagesFolder = (model.DestinationPath ?? string.Empty).Trim().Trim('"'); model.FaceExecutablePath = executablePath; model.FaceOutputFolderPath = outputFilePath; if (string.IsNullOrWhiteSpace(executablePath) || !File.Exists(executablePath)) { statusBlock.Text = "Percorso eseguibile non valido."; return; } if (string.IsNullOrWhiteSpace(imagesFolder) || !Directory.Exists(imagesFolder)) { statusBlock.Text = "Cartella Destinazione non valida."; return; } if (string.IsNullOrWhiteSpace(outputFilePath)) { statusBlock.Text = "Inserisci il file di output .pkl."; return; } if (!string.Equals(Path.GetExtension(outputFilePath), ".pkl", StringComparison.OrdinalIgnoreCase)) { statusBlock.Text = "Il file di output deve avere estensione .pkl."; return; } try { var outputDirectory = Path.GetDirectoryName(outputFilePath); if (!string.IsNullOrWhiteSpace(outputDirectory)) { Directory.CreateDirectory(outputDirectory); } } catch (Exception ex) { _logger.LogError(ex, "Unable to create face output directory for file: {OutputFilePath}", outputFilePath); statusBlock.Text = "Impossibile creare la cartella del file di output."; return; } runButton.IsEnabled = false; statusBlock.Text = "Esecuzione face encoder in corso..."; outputLogBox.Text = string.Empty; var outputLines = new StringBuilder(); var errorLines = new StringBuilder(); try { var imagesFolderArg = NormalizeDirectoryPathArgument(imagesFolder); var outputFileArg = NormalizeFilePathArgument(outputFilePath); var processStartInfo = new ProcessStartInfo { FileName = executablePath, WorkingDirectory = Path.GetDirectoryName(executablePath) ?? Environment.CurrentDirectory, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, }; processStartInfo.ArgumentList.Add("--images"); processStartInfo.ArgumentList.Add(imagesFolderArg); processStartInfo.ArgumentList.Add("--out"); processStartInfo.ArgumentList.Add(outputFileArg); using var process = new Process { StartInfo = processStartInfo, EnableRaisingEvents = true }; process.OutputDataReceived += (_, args) => { if (string.IsNullOrWhiteSpace(args.Data)) { return; } lock (outputLines) { outputLines.AppendLine(args.Data); } }; process.ErrorDataReceived += (_, args) => { if (string.IsNullOrWhiteSpace(args.Data)) { return; } lock (errorLines) { errorLines.AppendLine(args.Data); } }; if (!process.Start()) { throw new InvalidOperationException("Avvio face_encoder.exe fallito."); } process.BeginOutputReadLine(); process.BeginErrorReadLine(); await process.WaitForExitAsync().ConfigureAwait(true); var summary = new StringBuilder(); summary.AppendLine($"Exit code: {process.ExitCode}"); if (outputLines.Length > 0) { summary.AppendLine(); summary.AppendLine("STDOUT:"); summary.Append(outputLines); } if (errorLines.Length > 0) { summary.AppendLine(); summary.AppendLine("STDERR:"); summary.Append(errorLines); } outputLogBox.Text = summary.ToString(); statusBlock.Text = process.ExitCode == 0 ? "Face encoder completato." : $"Face encoder terminato con errore (code {process.ExitCode})."; } catch (Exception ex) { _logger.LogError(ex, "Face encoder execution failed."); outputLogBox.Text = ex.ToString(); statusBlock.Text = "Errore durante esecuzione face encoder."; } finally { runButton.IsEnabled = true; } } private static void OpenInExplorer(string? path) { if (string.IsNullOrWhiteSpace(path)) { return; } var normalizedPath = path.Trim().Trim('"'); try { if (File.Exists(normalizedPath)) { Process.Start("explorer.exe", $"/select,\"{normalizedPath}\""); } else if (Directory.Exists(normalizedPath)) { Process.Start(new ProcessStartInfo { FileName = normalizedPath, UseShellExecute = true }); } } catch { // Ignore failures when opening Explorer. } } private static string NormalizeDirectoryPathArgument(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } var normalized = value.Trim().Trim('"'); var root = Path.GetPathRoot(normalized); if (!string.IsNullOrEmpty(root) && normalized.Length > root.Length) { normalized = normalized.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); } return normalized; } private static string NormalizeFilePathArgument(string value) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } return value.Trim().Trim('"'); } }