feat: Implement Face AI functionality and race upload settings in Avalonia UI
This commit is contained in:
parent
15b1da4371
commit
6cf0c029fc
4 changed files with 528 additions and 191 deletions
|
|
@ -271,6 +271,45 @@
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<!-- Tab 7: Race Upload -->
|
<!-- Tab 7: Race Upload -->
|
||||||
|
<TabItem Header="Face AI">
|
||||||
|
<ScrollViewer>
|
||||||
|
<StackPanel Margin="8" Spacing="8">
|
||||||
|
<TextBlock Text="Face Recognition Encoder" FontWeight="Bold" />
|
||||||
|
<TextBlock Text="Esegue face_encoder.exe usando la cartella Destinazione corrente come --images."
|
||||||
|
TextWrapping="Wrap" Opacity="0.8" />
|
||||||
|
|
||||||
|
<TextBlock Text="Eseguibile" FontWeight="Bold" Margin="0,6,0,0" />
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="8">
|
||||||
|
<TextBlock Grid.Column="0" Text="face_encoder:" VerticalAlignment="Center" />
|
||||||
|
<TextBox Grid.Column="1" Name="FaceExecutablePathTextBox" Text="{Binding FaceExecutablePath, Mode=TwoWay}" Watermark="C:\\tools\\face_encoder.exe" />
|
||||||
|
<Button Grid.Column="2" Name="FaceSelectExecutableButton" Content="Scegli..." Click="SelectFaceExecutable_Click" Width="88" />
|
||||||
|
<Button Grid.Column="3" Name="FaceOpenExecutableButton" Content="Apri" Click="OpenFaceExecutableFolder_Click" Width="56" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBlock Text="Output encodings" FontWeight="Bold" Margin="0,6,0,0" />
|
||||||
|
<Grid ColumnDefinitions="Auto,*,Auto,Auto" ColumnSpacing="8">
|
||||||
|
<TextBlock Grid.Column="0" Text="Cartella out:" VerticalAlignment="Center" />
|
||||||
|
<TextBox Grid.Column="1" Name="FaceOutputFolderTextBox" Text="{Binding FaceOutputFolderPath, Mode=TwoWay}" Watermark="C:\\output\\encodings" />
|
||||||
|
<Button Grid.Column="2" Name="FaceSelectOutputButton" Content="Scegli..." Click="SelectFaceOutputFolder_Click" Width="88" />
|
||||||
|
<Button Grid.Column="3" Name="FaceOpenOutputButton" Content="Apri" Click="OpenFaceOutputFolder_Click" Width="56" />
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Spacing="8" Margin="0,8,0,0">
|
||||||
|
<Button Name="FaceRunButton" Content="Esegui Face Encoder" Click="RunFaceEncoder_Click" />
|
||||||
|
<TextBlock Name="FaceStatusTextBlock" VerticalAlignment="Center" />
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<TextBlock Text="Output comando" FontWeight="Bold" Margin="0,8,0,0" />
|
||||||
|
<TextBox Name="FaceOutputTextBox"
|
||||||
|
IsReadOnly="True"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
TextWrapping="Wrap"
|
||||||
|
MinHeight="180" />
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
|
<!-- Tab 8: Race Upload -->
|
||||||
<TabItem Header="Race Upload">
|
<TabItem Header="Race Upload">
|
||||||
<ScrollViewer>
|
<ScrollViewer>
|
||||||
<StackPanel Margin="8" Spacing="8">
|
<StackPanel Margin="8" Spacing="8">
|
||||||
|
|
@ -280,41 +319,41 @@
|
||||||
|
|
||||||
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Login:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Login:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="0" Grid.Column="1" Name="ApiLoginTextBox" Watermark="admin user" />
|
<TextBox Grid.Row="0" Grid.Column="1" Name="ApiLoginTextBox" Text="{Binding ApiLogin, Mode=TwoWay}" Watermark="admin user" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Password:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Password:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" Name="ApiPasswordTextBox" PasswordChar="*" />
|
<TextBox Grid.Row="1" Grid.Column="1" Name="ApiPasswordTextBox" Text="{Binding ApiPassword, Mode=TwoWay}" PasswordChar="*" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TextBlock Text="Dati gara" FontWeight="Bold" Margin="0,4,0,0" />
|
<TextBlock Text="Dati gara" FontWeight="Bold" Margin="0,4,0,0" />
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto,Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto,Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Descrizione:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Descrizione:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="0" Grid.Column="1" Name="ApiRaceDescriptionTextBox" Watermark="Nome gara" />
|
<TextBox Grid.Row="0" Grid.Column="1" Name="ApiRaceDescriptionTextBox" Text="{Binding ApiRaceDescription, Mode=TwoWay}" Watermark="Nome gara" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="2" Text="Tipo Gara ID:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="0" Grid.Column="2" Text="Tipo Gara ID:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="0" Grid.Column="3" Name="ApiRaceTypeIdTextBox" Text="1" />
|
<TextBox Grid.Row="0" Grid.Column="3" Name="ApiRaceTypeIdTextBox" Text="{Binding ApiRaceTypeId, Mode=TwoWay}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Data Inizio:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Data Inizio:" VerticalAlignment="Center" />
|
||||||
<CalendarDatePicker Grid.Row="1" Grid.Column="1" Name="ApiRaceStartDatePicker" />
|
<CalendarDatePicker Grid.Row="1" Grid.Column="1" Name="ApiRaceStartDatePicker" SelectedDate="{Binding ApiRaceStartDate, Mode=TwoWay}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="2" Text="Data Fine:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="1" Grid.Column="2" Text="Data Fine:" VerticalAlignment="Center" />
|
||||||
<CalendarDatePicker Grid.Row="1" Grid.Column="3" Name="ApiRaceEndDatePicker" />
|
<CalendarDatePicker Grid.Row="1" Grid.Column="3" Name="ApiRaceEndDatePicker" SelectedDate="{Binding ApiRaceEndDate, Mode=TwoWay}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Path Base Gara:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="2" Grid.Column="0" Text="Path Base Gara:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="2" Grid.Column="1" Name="ApiPathBaseTextBox" Watermark="2026/mia-gara/" />
|
<TextBox Grid.Row="2" Grid.Column="1" Name="ApiPathBaseTextBox" Text="{Binding ApiPathBase, Mode=TwoWay}" Watermark="2026/mia-gara/" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="2" Grid.Column="2" Text="Localita:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="2" Grid.Column="2" Text="Localita:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="2" Grid.Column="3" Name="ApiLocalitaTextBox" />
|
<TextBox Grid.Row="2" Grid.Column="3" Name="ApiLocalitaTextBox" Text="{Binding ApiLocalita, Mode=TwoWay}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Grid.Column="0" Text="Evento In Linea:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="3" Grid.Column="0" Text="Evento In Linea:" VerticalAlignment="Center" />
|
||||||
<ComboBox Grid.Row="3" Grid.Column="1" Name="ApiEventoInLineaComboBox" SelectedIndex="0">
|
<ComboBox Grid.Row="3" Grid.Column="1" Name="ApiEventoInLineaComboBox" SelectedIndex="{Binding ApiEventoInLineaIndex, Mode=TwoWay}">
|
||||||
<ComboBoxItem Content="0 - Non in linea" />
|
<ComboBoxItem Content="0 - Non in linea" />
|
||||||
<ComboBoxItem Content="1 - Stand by" />
|
<ComboBoxItem Content="1 - Stand by" />
|
||||||
<ComboBoxItem Content="2 - In linea" />
|
<ComboBoxItem Content="2 - In linea" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
<TextBlock Grid.Row="3" Grid.Column="2" Text="Tipo Indicizzazione:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="3" Grid.Column="2" Text="Tipo Indicizzazione:" VerticalAlignment="Center" />
|
||||||
<ComboBox Grid.Row="3" Grid.Column="3" Name="ApiTipoIndexComboBox" SelectedIndex="1">
|
<ComboBox Grid.Row="3" Grid.Column="3" Name="ApiTipoIndexComboBox" SelectedIndex="{Binding ApiTipoIndexValue, Mode=TwoWay}">
|
||||||
<ComboBoxItem Content="0" />
|
<ComboBoxItem Content="0" />
|
||||||
<ComboBoxItem Content="1" />
|
<ComboBoxItem Content="1" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
@ -322,16 +361,17 @@
|
||||||
|
|
||||||
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
<Grid ColumnDefinitions="Auto,*,Auto,*" RowDefinitions="Auto,Auto" ColumnSpacing="8" RowSpacing="8">
|
||||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="Evento Omaggio:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="0" Grid.Column="0" Text="Evento Omaggio:" VerticalAlignment="Center" />
|
||||||
<ComboBox Grid.Row="0" Grid.Column="1" Name="ApiFreeEventComboBox" SelectedIndex="0">
|
<ComboBox Grid.Row="0" Grid.Column="1" Name="ApiFreeEventComboBox" SelectedIndex="{Binding ApiFreeEventIndex, Mode=TwoWay}">
|
||||||
<ComboBoxItem Content="0 - No" />
|
<ComboBoxItem Content="0 - No" />
|
||||||
<ComboBoxItem Content="1 - SI" />
|
<ComboBoxItem Content="1 - SI" />
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
|
||||||
<TextBlock Grid.Row="0" Grid.Column="2" Text="id_gara corrente:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="0" Grid.Column="2" Text="id_gara corrente:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="0" Grid.Column="3" Name="ApiRaceIdTextBox" />
|
<TextBox Grid.Row="0" Grid.Column="3" Name="ApiRaceIdTextBox" Text="{Binding ApiRaceId, Mode=TwoWay}" />
|
||||||
|
|
||||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Path remoto processate:" VerticalAlignment="Center" />
|
<TextBlock Grid.Row="1" Grid.Column="0" Text="Path remoto processate:" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Name="ApiRemoteProcessedBasePathTextBox"
|
<TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Name="ApiRemoteProcessedBasePathTextBox"
|
||||||
|
Text="{Binding ApiRemoteProcessedBasePath, Mode=TwoWay}"
|
||||||
Watermark="/percorso/remoto/foto-ridotte" />
|
Watermark="/percorso/remoto/foto-ridotte" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
@ -356,7 +396,7 @@
|
||||||
<!-- Right: Controls and live info -->
|
<!-- Right: Controls and live info -->
|
||||||
<StackPanel Grid.Column="1">
|
<StackPanel Grid.Column="1">
|
||||||
<StackPanel HorizontalAlignment="Right" Margin="0,0,0,12">
|
<StackPanel HorizontalAlignment="Right" Margin="0,0,0,12">
|
||||||
<Button Width="28" Height="28" Click="ToggleTheme_Click" ToolTip.Tip="Cambia tema"
|
<Button Name="ThemeToggleButton" Width="28" Height="28" Click="ToggleTheme_Click" ToolTip.Tip="Cambia tema"
|
||||||
HorizontalAlignment="Right" Padding="2" Content="🌙" />
|
HorizontalAlignment="Right" Padding="2" Content="🌙" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border BorderBrush="#DDD" BorderThickness="1" Padding="8" MaxWidth="280">
|
<Border BorderBrush="#DDD" BorderThickness="1" Padding="8" MaxWidth="280">
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Catalog.Communication.Models;
|
||||||
using ImageCatalog;
|
using ImageCatalog;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
@ -22,35 +23,24 @@ namespace ImageCatalog_2;
|
||||||
|
|
||||||
public partial class AvaloniaMainWindow : Window
|
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 DataModel _model;
|
||||||
private readonly IRaceUploadCommunicationClient _apiClient;
|
private readonly IRaceUploadCommunicationClient _apiClient;
|
||||||
private readonly ParametriSetup _parametriSetup;
|
|
||||||
private readonly ILogger<AvaloniaMainWindow> _logger;
|
private readonly ILogger<AvaloniaMainWindow> _logger;
|
||||||
private bool _isDarkTheme = false;
|
private bool _isDarkTheme = false;
|
||||||
|
|
||||||
public AvaloniaMainWindow(
|
public AvaloniaMainWindow(
|
||||||
DataModel model,
|
DataModel model,
|
||||||
IRaceUploadCommunicationClient apiClient,
|
IRaceUploadCommunicationClient apiClient,
|
||||||
ParametriSetup parametriSetup,
|
|
||||||
ILogger<AvaloniaMainWindow> logger)
|
ILogger<AvaloniaMainWindow> logger)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_model = model;
|
_model = model;
|
||||||
_apiClient = apiClient;
|
_apiClient = apiClient;
|
||||||
_parametriSetup = parametriSetup;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
DataContext = _model;
|
DataContext = _model;
|
||||||
|
|
||||||
|
Opened += (_, _) => SyncThemeStateFromCurrentTheme();
|
||||||
|
|
||||||
// Provide Avalonia dispatcher so DataModel can marshal UI updates
|
// Provide Avalonia dispatcher so DataModel can marshal UI updates
|
||||||
_model.UiInvoker = action => Dispatcher.UIThread.Invoke(action);
|
_model.UiInvoker = action => Dispatcher.UIThread.Invoke(action);
|
||||||
|
|
||||||
|
|
@ -134,9 +124,6 @@ public partial class AvaloniaMainWindow : Window
|
||||||
if (e.PropertyName == nameof(_model.LogoFile))
|
if (e.PropertyName == nameof(_model.LogoFile))
|
||||||
UpdateLogoPreview(_model.LogoFile);
|
UpdateLogoPreview(_model.LogoFile);
|
||||||
};
|
};
|
||||||
|
|
||||||
LoadApiTestCredentials();
|
|
||||||
LoadRaceUploadSettings();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleTheme_Click(object? sender, RoutedEventArgs e)
|
private void ToggleTheme_Click(object? sender, RoutedEventArgs e)
|
||||||
|
|
@ -144,6 +131,8 @@ public partial class AvaloniaMainWindow : Window
|
||||||
_isDarkTheme = !_isDarkTheme;
|
_isDarkTheme = !_isDarkTheme;
|
||||||
if (Avalonia.Application.Current != null)
|
if (Avalonia.Application.Current != null)
|
||||||
Avalonia.Application.Current.RequestedThemeVariant = _isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light;
|
Avalonia.Application.Current.RequestedThemeVariant = _isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light;
|
||||||
|
|
||||||
|
UpdateThemeToggleButtonContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OpenSourceFolder_Click(object? sender, RoutedEventArgs e) => OpenInExplorer(_model.SourcePath);
|
private void OpenSourceFolder_Click(object? sender, RoutedEventArgs e) => OpenInExplorer(_model.SourcePath);
|
||||||
|
|
@ -182,133 +171,21 @@ public partial class AvaloniaMainWindow : Window
|
||||||
catch { preview.Source = null; }
|
catch { preview.Source = null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadApiTestCredentials()
|
|
||||||
{
|
|
||||||
var loginBox = this.FindControl<Avalonia.Controls.TextBox>("ApiLoginTextBox");
|
|
||||||
var passwordBox = this.FindControl<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("ApiLoginTextBox");
|
|
||||||
var passwordBox = this.FindControl<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("ApiRaceTypeIdTextBox");
|
|
||||||
var pathBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPathBaseTextBox");
|
|
||||||
var remoteBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRemoteProcessedBasePathTextBox");
|
|
||||||
var onlineBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiEventoInLineaComboBox");
|
|
||||||
var indexBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiTipoIndexComboBox");
|
|
||||||
var freeBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiFreeEventComboBox");
|
|
||||||
var raceIdBox = this.FindControl<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("ApiRaceTypeIdTextBox");
|
|
||||||
var pathBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPathBaseTextBox");
|
|
||||||
var remoteBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRemoteProcessedBasePathTextBox");
|
|
||||||
var onlineBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiEventoInLineaComboBox");
|
|
||||||
var indexBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiTipoIndexComboBox");
|
|
||||||
var freeBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiFreeEventComboBox");
|
|
||||||
var raceIdBox = this.FindControl<Avalonia.Controls.TextBox>("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)
|
private async void CreateRace_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var loginBox = this.FindControl<Avalonia.Controls.TextBox>("ApiLoginTextBox");
|
|
||||||
var passwordBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPasswordTextBox");
|
|
||||||
var raceTypeBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRaceTypeIdTextBox");
|
|
||||||
var raceDescriptionBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRaceDescriptionTextBox");
|
|
||||||
var raceStartPicker = this.FindControl<CalendarDatePicker>("ApiRaceStartDatePicker");
|
|
||||||
var raceEndPicker = this.FindControl<CalendarDatePicker>("ApiRaceEndDatePicker");
|
|
||||||
var pathBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPathBaseTextBox");
|
|
||||||
var localitaBox = this.FindControl<Avalonia.Controls.TextBox>("ApiLocalitaTextBox");
|
|
||||||
var raceIdBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRaceIdTextBox");
|
|
||||||
var eventoInLineaBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiEventoInLineaComboBox");
|
|
||||||
var tipoIndexBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiTipoIndexComboBox");
|
|
||||||
var freeBox = this.FindControl<Avalonia.Controls.ComboBox>("ApiFreeEventComboBox");
|
|
||||||
var outputBox = this.FindControl<Avalonia.Controls.TextBox>("ApiOutputTextBox");
|
var outputBox = this.FindControl<Avalonia.Controls.TextBox>("ApiOutputTextBox");
|
||||||
var statusBlock = this.FindControl<TextBlock>("ApiStatusTextBlock");
|
var statusBlock = this.FindControl<TextBlock>("ApiStatusTextBlock");
|
||||||
var createButton = this.FindControl<Avalonia.Controls.Button>("ApiCreateRaceButton");
|
var createButton = this.FindControl<Avalonia.Controls.Button>("ApiCreateRaceButton");
|
||||||
var uploadButton = this.FindControl<Avalonia.Controls.Button>("ApiUploadButton");
|
var uploadButton = this.FindControl<Avalonia.Controls.Button>("ApiUploadButton");
|
||||||
|
|
||||||
if (loginBox is null || passwordBox is null || raceTypeBox is null || raceDescriptionBox is null || raceStartPicker is null ||
|
if (outputBox is null || statusBlock is null || createButton is null || uploadButton 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)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var login = loginBox.Text?.Trim() ?? string.Empty;
|
var login = _model.ApiLogin?.Trim() ?? string.Empty;
|
||||||
var password = passwordBox.Text ?? string.Empty;
|
var password = _model.ApiPassword ?? string.Empty;
|
||||||
var descriptionRaw = raceDescriptionBox.Text?.Trim() ?? string.Empty;
|
var descriptionRaw = _model.ApiRaceDescription?.Trim() ?? string.Empty;
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(descriptionRaw))
|
if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(descriptionRaw))
|
||||||
{
|
{
|
||||||
|
|
@ -316,18 +193,12 @@ public partial class AvaloniaMainWindow : Window
|
||||||
return;
|
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.";
|
statusBlock.Text = "Tipo gara non valido.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!raceStartPicker.SelectedDate.HasValue)
|
|
||||||
{
|
|
||||||
statusBlock.Text = "Seleziona la data di inizio gara.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
createButton.IsEnabled = false;
|
createButton.IsEnabled = false;
|
||||||
uploadButton.IsEnabled = false;
|
uploadButton.IsEnabled = false;
|
||||||
statusBlock.Text = "Creazione gara in corso...";
|
statusBlock.Text = "Creazione gara in corso...";
|
||||||
|
|
@ -335,15 +206,10 @@ public partial class AvaloniaMainWindow : Window
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var startDate = DateOnly.FromDateTime(raceStartPicker.SelectedDate.Value.Date);
|
var startDate = DateOnly.FromDateTime(_model.ApiRaceStartDate.Date);
|
||||||
var endDate = raceEndPicker.SelectedDate.HasValue
|
var endDate = DateOnly.FromDateTime((_model.ApiRaceEndDate == default ? _model.ApiRaceStartDate : _model.ApiRaceEndDate).Date);
|
||||||
? DateOnly.FromDateTime(raceEndPicker.SelectedDate.Value.Date)
|
|
||||||
: startDate;
|
|
||||||
var sanitizedDescription = SanitizeRaceDescription(descriptionRaw);
|
var sanitizedDescription = SanitizeRaceDescription(descriptionRaw);
|
||||||
|
|
||||||
SaveApiTestCredentials();
|
|
||||||
SaveRaceUploadSettings();
|
|
||||||
|
|
||||||
var loginResponse = await LoginAsync(login, password).ConfigureAwait(true);
|
var loginResponse = await LoginAsync(login, password).ConfigureAwait(true);
|
||||||
|
|
||||||
var saveResponse = await _apiClient.SaveRaceAsync(
|
var saveResponse = await _apiClient.SaveRaceAsync(
|
||||||
|
|
@ -354,11 +220,11 @@ public partial class AvaloniaMainWindow : Window
|
||||||
StartDate = startDate,
|
StartDate = startDate,
|
||||||
EndDate = endDate,
|
EndDate = endDate,
|
||||||
TipoGaraId = tipoGaraId,
|
TipoGaraId = tipoGaraId,
|
||||||
EventoInLinea = GetComboSelection(eventoInLineaBox),
|
EventoInLinea = _model.ApiEventoInLineaIndex,
|
||||||
TipoIndicizzazione = GetComboSelection(tipoIndexBox),
|
TipoIndicizzazione = _model.ApiTipoIndexValue,
|
||||||
FreeEvent = GetComboSelection(freeBox),
|
FreeEvent = _model.ApiFreeEventIndex,
|
||||||
PathBase = pathBaseBox.Text?.Trim(),
|
PathBase = _model.ApiPathBase?.Trim(),
|
||||||
Localita = localitaBox.Text?.Trim(),
|
Localita = _model.ApiLocalita?.Trim(),
|
||||||
},
|
},
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
|
|
@ -368,8 +234,7 @@ public partial class AvaloniaMainWindow : Window
|
||||||
throw new InvalidOperationException("Impossibile ricavare id_gara dalla risposta di salvataggio.");
|
throw new InvalidOperationException("Impossibile ricavare id_gara dalla risposta di salvataggio.");
|
||||||
}
|
}
|
||||||
|
|
||||||
raceIdBox.Text = raceId.ToString();
|
_model.ApiRaceId = raceId.ToString();
|
||||||
SaveRaceUploadSettings();
|
|
||||||
|
|
||||||
var createPointsResponse = await _apiClient.CreateRacePointsAsync(raceId, CancellationToken.None);
|
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)
|
private async void UploadProcessed_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var loginBox = this.FindControl<Avalonia.Controls.TextBox>("ApiLoginTextBox");
|
|
||||||
var passwordBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPasswordTextBox");
|
|
||||||
var raceIdBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRaceIdTextBox");
|
|
||||||
var pathBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiPathBaseTextBox");
|
|
||||||
var remoteBaseBox = this.FindControl<Avalonia.Controls.TextBox>("ApiRemoteProcessedBasePathTextBox");
|
|
||||||
var outputBox = this.FindControl<Avalonia.Controls.TextBox>("ApiOutputTextBox");
|
var outputBox = this.FindControl<Avalonia.Controls.TextBox>("ApiOutputTextBox");
|
||||||
var statusBlock = this.FindControl<TextBlock>("ApiStatusTextBlock");
|
var statusBlock = this.FindControl<TextBlock>("ApiStatusTextBlock");
|
||||||
var createButton = this.FindControl<Avalonia.Controls.Button>("ApiCreateRaceButton");
|
var createButton = this.FindControl<Avalonia.Controls.Button>("ApiCreateRaceButton");
|
||||||
var uploadButton = this.FindControl<Avalonia.Controls.Button>("ApiUploadButton");
|
var uploadButton = this.FindControl<Avalonia.Controls.Button>("ApiUploadButton");
|
||||||
|
|
||||||
if (loginBox is null || passwordBox is null || raceIdBox is null || pathBaseBox is null || remoteBaseBox is null ||
|
if (outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
|
||||||
outputBox is null || statusBlock is null || createButton is null || uploadButton is null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var login = loginBox.Text?.Trim() ?? string.Empty;
|
var login = _model.ApiLogin?.Trim() ?? string.Empty;
|
||||||
var password = passwordBox.Text ?? string.Empty;
|
var password = _model.ApiPassword ?? string.Empty;
|
||||||
var racePathBase = pathBaseBox.Text?.Trim() ?? string.Empty;
|
var racePathBase = _model.ApiPathBase?.Trim() ?? string.Empty;
|
||||||
var remoteProcessedBase = remoteBaseBox.Text?.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.";
|
statusBlock.Text = "id_gara non valido.";
|
||||||
return;
|
return;
|
||||||
|
|
@ -450,8 +309,6 @@ public partial class AvaloniaMainWindow : Window
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SaveApiTestCredentials();
|
|
||||||
SaveRaceUploadSettings();
|
|
||||||
await LoginAsync(login, password).ConfigureAwait(true);
|
await LoginAsync(login, password).ConfigureAwait(true);
|
||||||
|
|
||||||
var files = Directory
|
var files = Directory
|
||||||
|
|
@ -602,6 +459,279 @@ public partial class AvaloniaMainWindow : Window
|
||||||
|| extension.Equals(".gif", StringComparison.OrdinalIgnoreCase);
|
|| extension.Equals(".gif", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async void SelectFaceExecutable_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var executableBox = this.FindControl<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("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<Avalonia.Controls.TextBox>("FaceOutputFolderTextBox");
|
||||||
|
if (outputBox is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenInExplorer(outputBox.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void RunFaceEncoder_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var executableBox = this.FindControl<Avalonia.Controls.TextBox>("FaceExecutablePathTextBox");
|
||||||
|
var outputFolderBox = this.FindControl<Avalonia.Controls.TextBox>("FaceOutputFolderTextBox");
|
||||||
|
var outputLogBox = this.FindControl<Avalonia.Controls.TextBox>("FaceOutputTextBox");
|
||||||
|
var statusBlock = this.FindControl<TextBlock>("FaceStatusTextBlock");
|
||||||
|
var runButton = this.FindControl<Avalonia.Controls.Button>("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<Avalonia.Controls.Button>("ThemeToggleButton");
|
||||||
|
if (toggleButton is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleButton.Content = _isDarkTheme ? "☀" : "🌙";
|
||||||
|
}
|
||||||
|
|
||||||
private static long ExtractRaceId(string html)
|
private static long ExtractRaceId(string html)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(html))
|
if (string.IsNullOrWhiteSpace(html))
|
||||||
|
|
|
||||||
|
|
@ -251,6 +251,112 @@ namespace ImageCatalog_2
|
||||||
set { _csvOutputPath = value; NotifyPropertyChanged(); }
|
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
|
// Preview results for DataGrid
|
||||||
private System.Collections.ObjectModel.ObservableCollection<AiResult> _previewResults = new();
|
private System.Collections.ObjectModel.ObservableCollection<AiResult> _previewResults = new();
|
||||||
public System.Collections.ObjectModel.ObservableCollection<AiResult> PreviewResults => _previewResults;
|
public System.Collections.ObjectModel.ObservableCollection<AiResult> PreviewResults => _previewResults;
|
||||||
|
|
|
||||||
|
|
@ -273,5 +273,66 @@ namespace ImageCatalog_2.Models
|
||||||
[JsonPropertyName("CsvOutputPath")]
|
[JsonPropertyName("CsvOutputPath")]
|
||||||
[XmlElement("AI_PercorsoCsv")]
|
[XmlElement("AI_PercorsoCsv")]
|
||||||
public string CsvOutputPath { get; set; }
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue