349 lines
10 KiB
C#
349 lines
10 KiB
C#
|
|
using MaddoShared;
|
||
|
|
using Microsoft.Extensions.Logging;
|
||
|
|
|
||
|
|
namespace CatalogLite;
|
||
|
|
|
||
|
|
public sealed class LiteCatalogViewModel : ViewModelBase
|
||
|
|
{
|
||
|
|
private readonly CatalogConfigurationLoader _configurationLoader;
|
||
|
|
private readonly PicSettings _picSettings;
|
||
|
|
private readonly ImageCreationService _imageCreationService;
|
||
|
|
private readonly ImageProcessingCoordinator _imageProcessingCoordinator;
|
||
|
|
private readonly ILogger<LiteCatalogViewModel> _logger;
|
||
|
|
private CatalogLiteConfiguration? _configuration;
|
||
|
|
private CancellationTokenSource? _processingTokenSource;
|
||
|
|
private string _configurationPath = string.Empty;
|
||
|
|
private string _sourcePath = string.Empty;
|
||
|
|
private string _destinationPath = string.Empty;
|
||
|
|
private string _processingStatus = "Carica una configurazione XML.";
|
||
|
|
private string _speedCounter = "-";
|
||
|
|
private int _processedImagesCount;
|
||
|
|
private int _totalImagesCount;
|
||
|
|
private int _progressBarValue;
|
||
|
|
private int _progressBarMaximum = 100;
|
||
|
|
private bool _isProcessing;
|
||
|
|
|
||
|
|
public LiteCatalogViewModel(
|
||
|
|
CatalogConfigurationLoader configurationLoader,
|
||
|
|
PicSettings picSettings,
|
||
|
|
ImageCreationService imageCreationService,
|
||
|
|
ImageProcessingCoordinator imageProcessingCoordinator,
|
||
|
|
ILogger<LiteCatalogViewModel> logger)
|
||
|
|
{
|
||
|
|
_configurationLoader = configurationLoader;
|
||
|
|
_picSettings = picSettings;
|
||
|
|
_imageCreationService = imageCreationService;
|
||
|
|
_imageProcessingCoordinator = imageProcessingCoordinator;
|
||
|
|
_logger = logger;
|
||
|
|
|
||
|
|
LoadConfigurationCommand = new AsyncCommand(RequestLoadConfigurationAsync, () => !IsProcessing);
|
||
|
|
SelectSourceFolderCommand = new AsyncCommand(RequestSourceFolderAsync, () => !IsProcessing);
|
||
|
|
SelectDestinationFolderCommand = new AsyncCommand(RequestDestinationFolderAsync, () => !IsProcessing);
|
||
|
|
StartProcessingCommand = new AsyncCommand(StartProcessingAsync, CanStartProcessing);
|
||
|
|
StopProcessingCommand = new AsyncCommand(StopProcessingAsync, () => IsProcessing);
|
||
|
|
}
|
||
|
|
|
||
|
|
public event EventHandler? LoadConfigurationRequested;
|
||
|
|
public event EventHandler? SelectSourceFolderRequested;
|
||
|
|
public event EventHandler? SelectDestinationFolderRequested;
|
||
|
|
public event EventHandler<LiteMessageEventArgs>? ShowMessageRequested;
|
||
|
|
|
||
|
|
public Action<Action>? UiInvoker { get; set; }
|
||
|
|
|
||
|
|
public AsyncCommand LoadConfigurationCommand { get; }
|
||
|
|
public AsyncCommand SelectSourceFolderCommand { get; }
|
||
|
|
public AsyncCommand SelectDestinationFolderCommand { get; }
|
||
|
|
public AsyncCommand StartProcessingCommand { get; }
|
||
|
|
public AsyncCommand StopProcessingCommand { get; }
|
||
|
|
|
||
|
|
public string ConfigurationPath
|
||
|
|
{
|
||
|
|
get => _configurationPath;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_configurationPath = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public string SourcePath
|
||
|
|
{
|
||
|
|
get => _sourcePath;
|
||
|
|
set
|
||
|
|
{
|
||
|
|
_sourcePath = NormalizeDirectoryPath(value);
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
RaiseCommandStates();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public string DestinationPath
|
||
|
|
{
|
||
|
|
get => _destinationPath;
|
||
|
|
set
|
||
|
|
{
|
||
|
|
_destinationPath = NormalizeDirectoryPath(value);
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
RaiseCommandStates();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public string ProcessingStatus
|
||
|
|
{
|
||
|
|
get => _processingStatus;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_processingStatus = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public string SpeedCounter
|
||
|
|
{
|
||
|
|
get => _speedCounter;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_speedCounter = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int ProcessedImagesCount
|
||
|
|
{
|
||
|
|
get => _processedImagesCount;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_processedImagesCount = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int TotalImagesCount
|
||
|
|
{
|
||
|
|
get => _totalImagesCount;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_totalImagesCount = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int ProgressBarValue
|
||
|
|
{
|
||
|
|
get => _progressBarValue;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_progressBarValue = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public int ProgressBarMaximum
|
||
|
|
{
|
||
|
|
get => _progressBarMaximum;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_progressBarMaximum = Math.Max(1, value);
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool IsProcessing
|
||
|
|
{
|
||
|
|
get => _isProcessing;
|
||
|
|
private set
|
||
|
|
{
|
||
|
|
_isProcessing = value;
|
||
|
|
NotifyPropertyChanged();
|
||
|
|
RaiseCommandStates();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public async Task LoadConfigurationFromFileAsync(string filePath)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var configuration = await Task.Run(() => _configurationLoader.Load(filePath, _picSettings)).ConfigureAwait(false);
|
||
|
|
|
||
|
|
RunOnUiThread(() =>
|
||
|
|
{
|
||
|
|
_configuration = configuration;
|
||
|
|
ConfigurationPath = configuration.FilePath;
|
||
|
|
SourcePath = configuration.SourcePath;
|
||
|
|
DestinationPath = configuration.DestinationPath;
|
||
|
|
ResetProgress("Configurazione caricata.");
|
||
|
|
});
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
_logger.LogError(ex, "Errore durante il caricamento della configurazione");
|
||
|
|
ShowMessage("Configurazione", $"Impossibile caricare la configurazione: {ex.GetBaseException().Message}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static string NormalizeDirectoryPath(string? path)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(path))
|
||
|
|
{
|
||
|
|
return string.Empty;
|
||
|
|
}
|
||
|
|
|
||
|
|
var trimmed = path.Trim().TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||
|
|
return trimmed + Path.DirectorySeparatorChar;
|
||
|
|
}
|
||
|
|
|
||
|
|
private Task RequestLoadConfigurationAsync()
|
||
|
|
{
|
||
|
|
LoadConfigurationRequested?.Invoke(this, EventArgs.Empty);
|
||
|
|
return Task.CompletedTask;
|
||
|
|
}
|
||
|
|
|
||
|
|
private Task RequestSourceFolderAsync()
|
||
|
|
{
|
||
|
|
SelectSourceFolderRequested?.Invoke(this, EventArgs.Empty);
|
||
|
|
return Task.CompletedTask;
|
||
|
|
}
|
||
|
|
|
||
|
|
private Task RequestDestinationFolderAsync()
|
||
|
|
{
|
||
|
|
SelectDestinationFolderRequested?.Invoke(this, EventArgs.Empty);
|
||
|
|
return Task.CompletedTask;
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool CanStartProcessing()
|
||
|
|
{
|
||
|
|
return !IsProcessing
|
||
|
|
&& _configuration is not null
|
||
|
|
&& !string.IsNullOrWhiteSpace(SourcePath)
|
||
|
|
&& !string.IsNullOrWhiteSpace(DestinationPath);
|
||
|
|
}
|
||
|
|
|
||
|
|
private async Task StartProcessingAsync()
|
||
|
|
{
|
||
|
|
if (_configuration is null)
|
||
|
|
{
|
||
|
|
ShowMessage("Configurazione", "Carica prima una configurazione XML.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!Directory.Exists(SourcePath))
|
||
|
|
{
|
||
|
|
ShowMessage("Sorgente", "La cartella sorgente non esiste.");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
Directory.CreateDirectory(DestinationPath);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
ShowMessage("Destinazione", $"Impossibile usare la cartella destinazione: {ex.GetBaseException().Message}");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
_processingTokenSource?.Dispose();
|
||
|
|
_processingTokenSource = new CancellationTokenSource();
|
||
|
|
var token = _processingTokenSource.Token;
|
||
|
|
|
||
|
|
var options = CatalogConfigurationLoader.CloneOptions(_configuration.Options, SourcePath, DestinationPath);
|
||
|
|
_picSettings.DirectorySorgente = SourcePath;
|
||
|
|
_picSettings.DirectoryDestinazione = DestinationPath;
|
||
|
|
_picSettings.DestDir = new DirectoryInfo(DestinationPath);
|
||
|
|
|
||
|
|
IsProcessing = true;
|
||
|
|
ResetProgress("Analisi immagini...");
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
var total = await Task.Run(() => _imageCreationService.GetFilesToProcessPublic(options).Count, token).ConfigureAwait(false);
|
||
|
|
RunOnUiThread(() =>
|
||
|
|
{
|
||
|
|
TotalImagesCount = total;
|
||
|
|
ProgressBarMaximum = Math.Max(1, total);
|
||
|
|
ProcessingStatus = total == 0 ? "Nessuna immagine trovata." : "Elaborazione in corso...";
|
||
|
|
});
|
||
|
|
|
||
|
|
if (total == 0)
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var result = await _imageProcessingCoordinator.RunAsync(
|
||
|
|
options,
|
||
|
|
token,
|
||
|
|
update => RunOnUiThread(() =>
|
||
|
|
{
|
||
|
|
ProcessedImagesCount = update.Processed;
|
||
|
|
TotalImagesCount = update.Total;
|
||
|
|
ProgressBarMaximum = update.Total;
|
||
|
|
ProgressBarValue = update.Processed;
|
||
|
|
ProcessingStatus = update.Status;
|
||
|
|
}),
|
||
|
|
speed => RunOnUiThread(() => SpeedCounter = speed)).ConfigureAwait(false);
|
||
|
|
|
||
|
|
RunOnUiThread(() =>
|
||
|
|
{
|
||
|
|
SpeedCounter = result.FinalSpeedCounter;
|
||
|
|
ProcessingStatus = "Finito.";
|
||
|
|
});
|
||
|
|
}
|
||
|
|
catch (OperationCanceledException)
|
||
|
|
{
|
||
|
|
RunOnUiThread(() => ProcessingStatus = "Operazione annullata.");
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
_logger.LogError(ex, "Errore durante l'elaborazione");
|
||
|
|
RunOnUiThread(() => ProcessingStatus = $"Errore: {ex.GetBaseException().Message}");
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
_processingTokenSource?.Dispose();
|
||
|
|
_processingTokenSource = null;
|
||
|
|
RunOnUiThread(() => IsProcessing = false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private Task StopProcessingAsync()
|
||
|
|
{
|
||
|
|
_processingTokenSource?.Cancel();
|
||
|
|
ProcessingStatus = "Arresto in corso...";
|
||
|
|
return Task.CompletedTask;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ResetProgress(string status)
|
||
|
|
{
|
||
|
|
ProcessingStatus = status;
|
||
|
|
ProcessedImagesCount = 0;
|
||
|
|
TotalImagesCount = 0;
|
||
|
|
ProgressBarValue = 0;
|
||
|
|
ProgressBarMaximum = 100;
|
||
|
|
SpeedCounter = "-";
|
||
|
|
}
|
||
|
|
|
||
|
|
private void RunOnUiThread(Action action)
|
||
|
|
{
|
||
|
|
if (UiInvoker is null)
|
||
|
|
{
|
||
|
|
action();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
UiInvoker(action);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void ShowMessage(string title, string message)
|
||
|
|
{
|
||
|
|
RunOnUiThread(() => ShowMessageRequested?.Invoke(this, new LiteMessageEventArgs(title, message)));
|
||
|
|
}
|
||
|
|
|
||
|
|
private void RaiseCommandStates()
|
||
|
|
{
|
||
|
|
LoadConfigurationCommand.RaiseCanExecuteChanged();
|
||
|
|
SelectSourceFolderCommand.RaiseCanExecuteChanged();
|
||
|
|
SelectDestinationFolderCommand.RaiseCanExecuteChanged();
|
||
|
|
StartProcessingCommand.RaiseCanExecuteChanged();
|
||
|
|
StopProcessingCommand.RaiseCanExecuteChanged();
|
||
|
|
}
|
||
|
|
}
|