diff --git a/imagecatalog/AvaloniaMainWindow.axaml b/imagecatalog/AvaloniaMainWindow.axaml
index dfc2baa..6165691 100644
--- a/imagecatalog/AvaloniaMainWindow.axaml
+++ b/imagecatalog/AvaloniaMainWindow.axaml
@@ -271,6 +271,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -280,41 +319,41 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -322,16 +361,17 @@
-
+
-
+
@@ -356,7 +396,7 @@
-
diff --git a/imagecatalog/AvaloniaMainWindow.axaml.cs b/imagecatalog/AvaloniaMainWindow.axaml.cs
index 396e48c..53bdba0 100644
--- a/imagecatalog/AvaloniaMainWindow.axaml.cs
+++ b/imagecatalog/AvaloniaMainWindow.axaml.cs
@@ -10,6 +10,7 @@ using Catalog.Communication.Models;
using ImageCatalog;
using Microsoft.Extensions.Logging;
using System;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
@@ -22,35 +23,24 @@ namespace ImageCatalog_2;
public partial class AvaloniaMainWindow : Window
{
- private const string ApiLoginKey = "ApiTest.Login";
- private const string ApiPasswordKey = "ApiTest.Password";
- private const string ApiRaceTypeKey = "RaceUpload.TipoGaraId";
- private const string ApiRacePathBaseKey = "RaceUpload.PathBase";
- private const string ApiRemoteProcessedBaseKey = "RaceUpload.RemoteProcessedBasePath";
- private const string ApiRaceOnlineFlagKey = "RaceUpload.FlgEventoInLinea";
- private const string ApiRaceIndexFlagKey = "RaceUpload.FlgTipoIndex";
- private const string ApiRaceFreeFlagKey = "RaceUpload.FlgFree";
- private const string ApiLastRaceIdKey = "RaceUpload.LastRaceId";
-
private readonly DataModel _model;
private readonly IRaceUploadCommunicationClient _apiClient;
- private readonly ParametriSetup _parametriSetup;
private readonly ILogger _logger;
private bool _isDarkTheme = false;
public AvaloniaMainWindow(
DataModel model,
IRaceUploadCommunicationClient apiClient,
- ParametriSetup parametriSetup,
ILogger logger)
{
InitializeComponent();
_model = model;
_apiClient = apiClient;
- _parametriSetup = parametriSetup;
_logger = logger;
DataContext = _model;
+ Opened += (_, _) => SyncThemeStateFromCurrentTheme();
+
// Provide Avalonia dispatcher so DataModel can marshal UI updates
_model.UiInvoker = action => Dispatcher.UIThread.Invoke(action);
@@ -134,9 +124,6 @@ public partial class AvaloniaMainWindow : Window
if (e.PropertyName == nameof(_model.LogoFile))
UpdateLogoPreview(_model.LogoFile);
};
-
- LoadApiTestCredentials();
- LoadRaceUploadSettings();
}
private void ToggleTheme_Click(object? sender, RoutedEventArgs e)
@@ -144,6 +131,8 @@ public partial class AvaloniaMainWindow : Window
_isDarkTheme = !_isDarkTheme;
if (Avalonia.Application.Current != null)
Avalonia.Application.Current.RequestedThemeVariant = _isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light;
+
+ UpdateThemeToggleButtonContent();
}
private void OpenSourceFolder_Click(object? sender, RoutedEventArgs e) => OpenInExplorer(_model.SourcePath);
@@ -182,133 +171,21 @@ public partial class AvaloniaMainWindow : Window
catch { preview.Source = null; }
}
- private void LoadApiTestCredentials()
- {
- var loginBox = this.FindControl("ApiLoginTextBox");
- var passwordBox = this.FindControl("ApiPasswordTextBox");
- if (loginBox is null || passwordBox is null)
- {
- return;
- }
-
- loginBox.Text = _parametriSetup.LeggiParametroString(ApiLoginKey);
- passwordBox.Text = _parametriSetup.LeggiParametroString(ApiPasswordKey);
- }
-
- private void SaveApiTestCredentials()
- {
- var loginBox = this.FindControl("ApiLoginTextBox");
- var passwordBox = this.FindControl("ApiPasswordTextBox");
- if (loginBox is null || passwordBox is null)
- {
- return;
- }
-
- _parametriSetup.AggiornaParametro(ApiLoginKey, loginBox.Text ?? string.Empty);
- _parametriSetup.AggiornaParametro(ApiPasswordKey, passwordBox.Text ?? string.Empty);
- _parametriSetup.SalvaParametriSetup();
- }
-
- private void LoadRaceUploadSettings()
- {
- var raceTypeBox = this.FindControl("ApiRaceTypeIdTextBox");
- var pathBaseBox = this.FindControl("ApiPathBaseTextBox");
- var remoteBaseBox = this.FindControl("ApiRemoteProcessedBasePathTextBox");
- var onlineBox = this.FindControl("ApiEventoInLineaComboBox");
- var indexBox = this.FindControl("ApiTipoIndexComboBox");
- var freeBox = this.FindControl("ApiFreeEventComboBox");
- var raceIdBox = this.FindControl("ApiRaceIdTextBox");
-
- if (raceTypeBox is not null)
- {
- raceTypeBox.Text = _parametriSetup.LeggiParametroString(ApiRaceTypeKey);
- }
-
- if (pathBaseBox is not null)
- {
- pathBaseBox.Text = _parametriSetup.LeggiParametroString(ApiRacePathBaseKey);
- }
-
- if (remoteBaseBox is not null)
- {
- remoteBaseBox.Text = _parametriSetup.LeggiParametroString(ApiRemoteProcessedBaseKey);
- }
-
- SetComboSelection(onlineBox, _parametriSetup.LeggiParametro(ApiRaceOnlineFlagKey, 0));
- SetComboSelection(indexBox, _parametriSetup.LeggiParametro(ApiRaceIndexFlagKey, 1));
- SetComboSelection(freeBox, _parametriSetup.LeggiParametro(ApiRaceFreeFlagKey, 0));
-
- if (raceIdBox is not null)
- {
- raceIdBox.Text = _parametriSetup.LeggiParametroString(ApiLastRaceIdKey);
- }
- }
-
- private void SaveRaceUploadSettings()
- {
- var raceTypeBox = this.FindControl("ApiRaceTypeIdTextBox");
- var pathBaseBox = this.FindControl("ApiPathBaseTextBox");
- var remoteBaseBox = this.FindControl("ApiRemoteProcessedBasePathTextBox");
- var onlineBox = this.FindControl("ApiEventoInLineaComboBox");
- var indexBox = this.FindControl("ApiTipoIndexComboBox");
- var freeBox = this.FindControl("ApiFreeEventComboBox");
- var raceIdBox = this.FindControl("ApiRaceIdTextBox");
-
- _parametriSetup.AggiornaParametro(ApiRaceTypeKey, raceTypeBox?.Text ?? string.Empty);
- _parametriSetup.AggiornaParametro(ApiRacePathBaseKey, pathBaseBox?.Text ?? string.Empty);
- _parametriSetup.AggiornaParametro(ApiRemoteProcessedBaseKey, remoteBaseBox?.Text ?? string.Empty);
- _parametriSetup.AggiornaParametro(ApiRaceOnlineFlagKey, GetComboSelection(onlineBox).ToString());
- _parametriSetup.AggiornaParametro(ApiRaceIndexFlagKey, GetComboSelection(indexBox).ToString());
- _parametriSetup.AggiornaParametro(ApiRaceFreeFlagKey, GetComboSelection(freeBox).ToString());
- _parametriSetup.AggiornaParametro(ApiLastRaceIdKey, raceIdBox?.Text ?? string.Empty);
- _parametriSetup.SalvaParametriSetup();
- }
-
- private static void SetComboSelection(Avalonia.Controls.ComboBox? comboBox, int value)
- {
- if (comboBox is null)
- {
- return;
- }
-
- comboBox.SelectedIndex = value;
- }
-
- private static int GetComboSelection(Avalonia.Controls.ComboBox? comboBox)
- {
- return comboBox?.SelectedIndex is int index && index >= 0 ? index : 0;
- }
-
private async void CreateRace_Click(object? sender, RoutedEventArgs e)
{
- var loginBox = this.FindControl("ApiLoginTextBox");
- var passwordBox = this.FindControl("ApiPasswordTextBox");
- var raceTypeBox = this.FindControl("ApiRaceTypeIdTextBox");
- var raceDescriptionBox = this.FindControl("ApiRaceDescriptionTextBox");
- var raceStartPicker = this.FindControl("ApiRaceStartDatePicker");
- var raceEndPicker = this.FindControl("ApiRaceEndDatePicker");
- var pathBaseBox = this.FindControl("ApiPathBaseTextBox");
- var localitaBox = this.FindControl("ApiLocalitaTextBox");
- var raceIdBox = this.FindControl("ApiRaceIdTextBox");
- var eventoInLineaBox = this.FindControl("ApiEventoInLineaComboBox");
- var tipoIndexBox = this.FindControl("ApiTipoIndexComboBox");
- var freeBox = this.FindControl("ApiFreeEventComboBox");
var outputBox = this.FindControl("ApiOutputTextBox");
var statusBlock = this.FindControl("ApiStatusTextBlock");
var createButton = this.FindControl("ApiCreateRaceButton");
var uploadButton = this.FindControl("ApiUploadButton");
- if (loginBox is null || passwordBox is null || raceTypeBox is null || raceDescriptionBox is null || raceStartPicker is null ||
- raceEndPicker is null || pathBaseBox is null || localitaBox is null || raceIdBox is null ||
- eventoInLineaBox is null || tipoIndexBox is null || freeBox is null ||
- outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
+ if (outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
{
return;
}
- var login = loginBox.Text?.Trim() ?? string.Empty;
- var password = passwordBox.Text ?? string.Empty;
- var descriptionRaw = raceDescriptionBox.Text?.Trim() ?? string.Empty;
+ var login = _model.ApiLogin?.Trim() ?? string.Empty;
+ var password = _model.ApiPassword ?? string.Empty;
+ var descriptionRaw = _model.ApiRaceDescription?.Trim() ?? string.Empty;
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(descriptionRaw))
{
@@ -316,18 +193,12 @@ public partial class AvaloniaMainWindow : Window
return;
}
- if (!long.TryParse(raceTypeBox.Text?.Trim(), out var tipoGaraId) || tipoGaraId <= 0)
+ if (!long.TryParse(_model.ApiRaceTypeId?.Trim(), out var tipoGaraId) || tipoGaraId <= 0)
{
statusBlock.Text = "Tipo gara non valido.";
return;
}
- if (!raceStartPicker.SelectedDate.HasValue)
- {
- statusBlock.Text = "Seleziona la data di inizio gara.";
- return;
- }
-
createButton.IsEnabled = false;
uploadButton.IsEnabled = false;
statusBlock.Text = "Creazione gara in corso...";
@@ -335,15 +206,10 @@ public partial class AvaloniaMainWindow : Window
try
{
- var startDate = DateOnly.FromDateTime(raceStartPicker.SelectedDate.Value.Date);
- var endDate = raceEndPicker.SelectedDate.HasValue
- ? DateOnly.FromDateTime(raceEndPicker.SelectedDate.Value.Date)
- : startDate;
+ var startDate = DateOnly.FromDateTime(_model.ApiRaceStartDate.Date);
+ var endDate = DateOnly.FromDateTime((_model.ApiRaceEndDate == default ? _model.ApiRaceStartDate : _model.ApiRaceEndDate).Date);
var sanitizedDescription = SanitizeRaceDescription(descriptionRaw);
- SaveApiTestCredentials();
- SaveRaceUploadSettings();
-
var loginResponse = await LoginAsync(login, password).ConfigureAwait(true);
var saveResponse = await _apiClient.SaveRaceAsync(
@@ -354,11 +220,11 @@ public partial class AvaloniaMainWindow : Window
StartDate = startDate,
EndDate = endDate,
TipoGaraId = tipoGaraId,
- EventoInLinea = GetComboSelection(eventoInLineaBox),
- TipoIndicizzazione = GetComboSelection(tipoIndexBox),
- FreeEvent = GetComboSelection(freeBox),
- PathBase = pathBaseBox.Text?.Trim(),
- Localita = localitaBox.Text?.Trim(),
+ EventoInLinea = _model.ApiEventoInLineaIndex,
+ TipoIndicizzazione = _model.ApiTipoIndexValue,
+ FreeEvent = _model.ApiFreeEventIndex,
+ PathBase = _model.ApiPathBase?.Trim(),
+ Localita = _model.ApiLocalita?.Trim(),
},
CancellationToken.None);
@@ -368,8 +234,7 @@ public partial class AvaloniaMainWindow : Window
throw new InvalidOperationException("Impossibile ricavare id_gara dalla risposta di salvataggio.");
}
- raceIdBox.Text = raceId.ToString();
- SaveRaceUploadSettings();
+ _model.ApiRaceId = raceId.ToString();
var createPointsResponse = await _apiClient.CreateRacePointsAsync(raceId, CancellationToken.None);
@@ -399,28 +264,22 @@ public partial class AvaloniaMainWindow : Window
private async void UploadProcessed_Click(object? sender, RoutedEventArgs e)
{
- var loginBox = this.FindControl("ApiLoginTextBox");
- var passwordBox = this.FindControl("ApiPasswordTextBox");
- var raceIdBox = this.FindControl("ApiRaceIdTextBox");
- var pathBaseBox = this.FindControl("ApiPathBaseTextBox");
- var remoteBaseBox = this.FindControl("ApiRemoteProcessedBasePathTextBox");
var outputBox = this.FindControl("ApiOutputTextBox");
var statusBlock = this.FindControl("ApiStatusTextBlock");
var createButton = this.FindControl("ApiCreateRaceButton");
var uploadButton = this.FindControl("ApiUploadButton");
- if (loginBox is null || passwordBox is null || raceIdBox is null || pathBaseBox is null || remoteBaseBox is null ||
- outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
+ if (outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
{
return;
}
- var login = loginBox.Text?.Trim() ?? string.Empty;
- var password = passwordBox.Text ?? string.Empty;
- var racePathBase = pathBaseBox.Text?.Trim() ?? string.Empty;
- var remoteProcessedBase = remoteBaseBox.Text?.Trim() ?? string.Empty;
+ var login = _model.ApiLogin?.Trim() ?? string.Empty;
+ var password = _model.ApiPassword ?? string.Empty;
+ var racePathBase = _model.ApiPathBase?.Trim() ?? string.Empty;
+ var remoteProcessedBase = _model.ApiRemoteProcessedBasePath?.Trim() ?? string.Empty;
- if (!long.TryParse(raceIdBox.Text?.Trim(), out var raceId) || raceId <= 0)
+ if (!long.TryParse(_model.ApiRaceId?.Trim(), out var raceId) || raceId <= 0)
{
statusBlock.Text = "id_gara non valido.";
return;
@@ -450,8 +309,6 @@ public partial class AvaloniaMainWindow : Window
try
{
- SaveApiTestCredentials();
- SaveRaceUploadSettings();
await LoginAsync(login, password).ConfigureAwait(true);
var files = Directory
@@ -602,6 +459,279 @@ public partial class AvaloniaMainWindow : Window
|| extension.Equals(".gif", StringComparison.OrdinalIgnoreCase);
}
+ private async void SelectFaceExecutable_Click(object? sender, RoutedEventArgs e)
+ {
+ var executableBox = this.FindControl("FaceExecutablePathTextBox");
+ if (executableBox is null)
+ {
+ return;
+ }
+
+ var files = await StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
+ {
+ Title = "Seleziona face_encoder.exe",
+ FileTypeFilter = new[]
+ {
+ new FilePickerFileType("Eseguibile") { Patterns = new[] { "*.exe" } },
+ new FilePickerFileType("Tutti i file") { Patterns = new[] { "*.*" } }
+ }
+ });
+
+ if (files.Count > 0)
+ {
+ executableBox.Text = files[0].Path.LocalPath;
+ _model.FaceExecutablePath = executableBox.Text;
+ }
+ }
+
+ private async void SelectFaceOutputFolder_Click(object? sender, RoutedEventArgs e)
+ {
+ var outputBox = this.FindControl("FaceOutputFolderTextBox");
+ if (outputBox is null)
+ {
+ return;
+ }
+
+ var folders = await StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
+ {
+ Title = "Seleziona cartella output encodings"
+ });
+
+ if (folders.Count > 0)
+ {
+ outputBox.Text = folders[0].Path.LocalPath;
+ _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 dir = Path.GetDirectoryName(path);
+ OpenInExplorer(string.IsNullOrWhiteSpace(dir) ? path : dir);
+ }
+
+ private void OpenFaceOutputFolder_Click(object? sender, RoutedEventArgs e)
+ {
+ var outputBox = this.FindControl("FaceOutputFolderTextBox");
+ if (outputBox is null)
+ {
+ return;
+ }
+
+ OpenInExplorer(outputBox.Text);
+ }
+
+ 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;
+ }
+
+ var executablePath = executableBox.Text?.Trim().Trim('"') ?? string.Empty;
+ var outputFolder = outputFolderBox.Text?.Trim().Trim('"') ?? string.Empty;
+ var imagesFolder = (_model.DestinationPath ?? string.Empty).Trim().Trim('"');
+
+ _model.FaceExecutablePath = executablePath;
+ _model.FaceOutputFolderPath = outputFolder;
+
+ 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(outputFolder))
+ {
+ statusBlock.Text = "Inserisci la cartella di output.";
+ return;
+ }
+
+ try
+ {
+ Directory.CreateDirectory(outputFolder);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Unable to create face output folder: {OutputFolder}", outputFolder);
+ statusBlock.Text = "Impossibile creare la cartella 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 outputFolderArg = NormalizeDirectoryPathArgument(outputFolder);
+ Console.WriteLine($"[FaceAI] Command: \"{executablePath}\" --images \"{imagesFolderArg}\" --out \"{outputFolderArg}\"");
+
+ 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(outputFolderArg);
+
+ using var process = new Process { StartInfo = processStartInfo, EnableRaisingEvents = true };
+ process.OutputDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrWhiteSpace(args.Data))
+ {
+ return;
+ }
+
+ lock (outputLines)
+ {
+ outputLines.AppendLine(args.Data);
+ }
+
+ Console.WriteLine(args.Data);
+ };
+
+ process.ErrorDataReceived += (_, args) =>
+ {
+ if (string.IsNullOrWhiteSpace(args.Data))
+ {
+ return;
+ }
+
+ lock (errorLines)
+ {
+ errorLines.AppendLine(args.Data);
+ }
+
+ Console.Error.WriteLine(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();
+
+ if (process.ExitCode == 0)
+ {
+ statusBlock.Text = "Face encoder completato.";
+ }
+ else
+ {
+ statusBlock.Text = $"Face encoder terminato con errore (code {process.ExitCode}).";
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Face encoder execution failed.");
+ Console.Error.WriteLine(ex);
+ outputLogBox.Text = ex.ToString();
+ statusBlock.Text = "Errore durante esecuzione face encoder.";
+ }
+ finally
+ {
+ runButton.IsEnabled = true;
+ }
+ }
+
+ 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 void SyncThemeStateFromCurrentTheme()
+ {
+ var actualVariant = ActualThemeVariant;
+ _isDarkTheme = actualVariant == ThemeVariant.Dark;
+ UpdateThemeToggleButtonContent();
+ }
+
+ private void UpdateThemeToggleButtonContent()
+ {
+ var toggleButton = this.FindControl("ThemeToggleButton");
+ if (toggleButton is null)
+ {
+ return;
+ }
+
+ toggleButton.Content = _isDarkTheme ? "☀" : "🌙";
+ }
+
private static long ExtractRaceId(string html)
{
if (string.IsNullOrWhiteSpace(html))
diff --git a/imagecatalog/DataModel.cs b/imagecatalog/DataModel.cs
index c7b0300..66fbb3f 100644
--- a/imagecatalog/DataModel.cs
+++ b/imagecatalog/DataModel.cs
@@ -51,8 +51,8 @@ namespace ImageCatalog_2
public List VerticalPositions { get; } = new() { "Alto", "Centro", "Basso" };
public List HorizontalAlignments { get; } = new() { "Sinistra", "Centro", "Destra" };
- public DataModel(ITestService testService, ISettingsService settingsService,
- ImageCreationService imageCreationService, PicSettings picSettings,
+ public DataModel(ITestService testService, ISettingsService settingsService,
+ ImageCreationService imageCreationService, PicSettings picSettings,
IMapper mapper, ILogger logger, MaddoShared.IVersionProvider? versionProvider = null)
{
_service = testService;
@@ -70,7 +70,7 @@ namespace ImageCatalog_2
ProcessImagesCommand = new AsyncCommand(ProcessImages);
SelectModelsFolderCommand = new RelayCommand(SelectModelsFolder);
SelectCsvOutputCommand = new RelayCommand(SelectCsvOutput);
-
+
SelectSourceFolderCommand = new RelayCommand(SelectSourceFolder);
SelectDestinationFolderCommand = new RelayCommand(SelectDestinationFolder);
SelectLogoFileCommand = new RelayCommand(SelectLogoFile);
@@ -251,6 +251,112 @@ namespace ImageCatalog_2
set { _csvOutputPath = value; NotifyPropertyChanged(); }
}
+ private string _faceExecutablePath = string.Empty;
+ public string FaceExecutablePath
+ {
+ get => _faceExecutablePath;
+ set { _faceExecutablePath = value; NotifyPropertyChanged(); }
+ }
+
+ private string _faceOutputFolderPath = string.Empty;
+ public string FaceOutputFolderPath
+ {
+ get => _faceOutputFolderPath;
+ set { _faceOutputFolderPath = value; NotifyPropertyChanged(); }
+ }
+
+ // Race upload settings
+ private string _apiLogin = string.Empty;
+ public string ApiLogin
+ {
+ get => _apiLogin;
+ set { _apiLogin = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiPassword = string.Empty;
+ public string ApiPassword
+ {
+ get => _apiPassword;
+ set { _apiPassword = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiRaceDescription = string.Empty;
+ public string ApiRaceDescription
+ {
+ get => _apiRaceDescription;
+ set { _apiRaceDescription = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiRaceTypeId = "1";
+ public string ApiRaceTypeId
+ {
+ get => _apiRaceTypeId;
+ set { _apiRaceTypeId = value; NotifyPropertyChanged(); }
+ }
+
+ private DateTime _apiRaceStartDate = DateTime.Today;
+ public DateTime ApiRaceStartDate
+ {
+ get => _apiRaceStartDate;
+ set { _apiRaceStartDate = value; NotifyPropertyChanged(); }
+ }
+
+ private DateTime _apiRaceEndDate = DateTime.Today;
+ public DateTime ApiRaceEndDate
+ {
+ get => _apiRaceEndDate;
+ set { _apiRaceEndDate = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiPathBase = string.Empty;
+ public string ApiPathBase
+ {
+ get => _apiPathBase;
+ set { _apiPathBase = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiLocalita = string.Empty;
+ public string ApiLocalita
+ {
+ get => _apiLocalita;
+ set { _apiLocalita = value; NotifyPropertyChanged(); }
+ }
+
+ private int _apiEventoInLineaIndex = 0;
+ public int ApiEventoInLineaIndex
+ {
+ get => _apiEventoInLineaIndex;
+ set { _apiEventoInLineaIndex = value; NotifyPropertyChanged(); }
+ }
+
+ private int _apiTipoIndexValue = 1;
+ public int ApiTipoIndexValue
+ {
+ get => _apiTipoIndexValue;
+ set { _apiTipoIndexValue = value; NotifyPropertyChanged(); }
+ }
+
+ private int _apiFreeEventIndex = 0;
+ public int ApiFreeEventIndex
+ {
+ get => _apiFreeEventIndex;
+ set { _apiFreeEventIndex = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiRaceId = string.Empty;
+ public string ApiRaceId
+ {
+ get => _apiRaceId;
+ set { _apiRaceId = value; NotifyPropertyChanged(); }
+ }
+
+ private string _apiRemoteProcessedBasePath = string.Empty;
+ public string ApiRemoteProcessedBasePath
+ {
+ get => _apiRemoteProcessedBasePath;
+ set { _apiRemoteProcessedBasePath = value; NotifyPropertyChanged(); }
+ }
+
// Preview results for DataGrid
private System.Collections.ObjectModel.ObservableCollection _previewResults = new();
public System.Collections.ObjectModel.ObservableCollection PreviewResults => _previewResults;
@@ -1266,7 +1372,7 @@ namespace ImageCatalog_2
// Fix paths
FixPaths();
-
+
// Reset counters
ProcessingStatus = "Elaborazione in corso...";
TotalImagesCount = 0;
@@ -1274,7 +1380,7 @@ namespace ImageCatalog_2
SpeedCounter = "-f/s";
ProgressBarValue = 0;
ProgressBarMaximum = 100;
-
+
// Update PicSettings from DataModel using AutoMapper
_mapper.Map(this, _picSettings);
// Explicitly ensure thumbnail-related flags are applied to PicSettings
@@ -1295,7 +1401,7 @@ namespace ImageCatalog_2
{
// Best-effort; do not fail processing on mapping issues
}
-
+
var imageCreationOptions = new ImageCreationService.Options
{
AggiornaSottodirectory = UpdateSubdirectories,
@@ -1317,16 +1423,16 @@ namespace ImageCatalog_2
_currentAmount = 0;
_previousAmount = 0;
_processedAtomic = 0;
-
+
// Start speed timer (sample every second using lightweight atomic reads)
_speedWatch = Stopwatch.StartNew();
_recentDiffs.Clear();
_speedTimer = new System.Threading.Timer(UpdateSpeedCounter, null, TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
var time = await _imageCreationService.CreaCatalogoParallel(
- imageCreationOptions,
- _results,
- OnImageProcessed,
+ imageCreationOptions,
+ _results,
+ OnImageProcessed,
token);
// AI integration stub: if ExtractNumbers is enabled, simulate or invoke OCR processing
@@ -1345,7 +1451,7 @@ namespace ImageCatalog_2
_logger.LogError(ex, "AI extraction failed");
}
}
-
+
// Compute final averages and show only averages (do not show raw seconds)
var finalProcessed = System.Threading.Volatile.Read(ref _processedAtomic);
double overallAvg = 0.0;
diff --git a/imagecatalog/Models/SettingsDto.cs b/imagecatalog/Models/SettingsDto.cs
index 0372180..7d56b15 100644
--- a/imagecatalog/Models/SettingsDto.cs
+++ b/imagecatalog/Models/SettingsDto.cs
@@ -273,5 +273,66 @@ namespace ImageCatalog_2.Models
[JsonPropertyName("CsvOutputPath")]
[XmlElement("AI_PercorsoCsv")]
public string CsvOutputPath { get; set; }
+
+ [JsonPropertyName("FaceExecutablePath")]
+ [XmlElement("AI_FaceExecutablePath")]
+ public string FaceExecutablePath { get; set; } = string.Empty;
+
+ [JsonPropertyName("FaceOutputFolderPath")]
+ [XmlElement("AI_FaceOutputFolderPath")]
+ public string FaceOutputFolderPath { get; set; } = string.Empty;
+
+ // Race upload settings
+ [JsonPropertyName("ApiLogin")]
+ [XmlElement("RaceUpload_Login")]
+ public string ApiLogin { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiPassword")]
+ [XmlElement("RaceUpload_Password")]
+ public string ApiPassword { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiRaceDescription")]
+ [XmlElement("RaceUpload_Description")]
+ public string ApiRaceDescription { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiRaceTypeId")]
+ [XmlElement("RaceUpload_TipoGaraId")]
+ public string ApiRaceTypeId { get; set; } = "1";
+
+ [JsonPropertyName("ApiRaceStartDate")]
+ [XmlElement("RaceUpload_StartDate")]
+ public DateTime ApiRaceStartDate { get; set; } = DateTime.Today;
+
+ [JsonPropertyName("ApiRaceEndDate")]
+ [XmlElement("RaceUpload_EndDate")]
+ public DateTime ApiRaceEndDate { get; set; } = DateTime.Today;
+
+ [JsonPropertyName("ApiPathBase")]
+ [XmlElement("RaceUpload_PathBase")]
+ public string ApiPathBase { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiLocalita")]
+ [XmlElement("RaceUpload_Localita")]
+ public string ApiLocalita { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiEventoInLineaIndex")]
+ [XmlElement("RaceUpload_EventoInLinea")]
+ public int ApiEventoInLineaIndex { get; set; } = 0;
+
+ [JsonPropertyName("ApiTipoIndexValue")]
+ [XmlElement("RaceUpload_TipoIndex")]
+ public int ApiTipoIndexValue { get; set; } = 1;
+
+ [JsonPropertyName("ApiFreeEventIndex")]
+ [XmlElement("RaceUpload_FreeEvent")]
+ public int ApiFreeEventIndex { get; set; } = 0;
+
+ [JsonPropertyName("ApiRaceId")]
+ [XmlElement("RaceUpload_LastRaceId")]
+ public string ApiRaceId { get; set; } = string.Empty;
+
+ [JsonPropertyName("ApiRemoteProcessedBasePath")]
+ [XmlElement("RaceUpload_RemoteProcessedBasePath")]
+ public string ApiRemoteProcessedBasePath { get; set; } = string.Empty;
}
}