Improve uploader client connection and diagnostics
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-12 20:40:40 +01:00
parent 3ee23f3a66
commit c8d1ac7971
10 changed files with 306 additions and 15 deletions

View File

@@ -3,9 +3,12 @@ using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform.Storage;
using Avalonia.Platform;
using Avalonia.Threading;
using PhotoboothUploader.Models;
using PhotoboothUploader.Services;
@@ -22,6 +25,10 @@ public partial class MainWindow : Window
private FileSystemWatcher? _watcher;
private readonly Dictionary<string, UploadItem> _uploadsByPath = new(StringComparer.OrdinalIgnoreCase);
private readonly HashSet<string> _failedPaths = new(StringComparer.OrdinalIgnoreCase);
private readonly string _userAgent;
private int _queuedCount;
private int _uploadingCount;
private int _failedCount;
public ObservableCollection<UploadItem> RecentUploads { get; } = new();
@@ -29,8 +36,10 @@ public partial class MainWindow : Window
{
InitializeComponent();
_settings = _settingsStore.Load();
_settings.BaseUrl ??= DefaultBaseUrl;
_client = new PhotoboothConnectClient(_settings.BaseUrl);
_settings.BaseUrl = NormalizeBaseUrl(_settings.BaseUrl) ?? DefaultBaseUrl;
_userAgent = $"FotospielPhotoboothUploader/{GetAppVersion()}";
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
_uploadService.Configure(_userAgent);
_settingsStore.Save(_settings);
DataContext = this;
ApplySettings();
@@ -38,6 +47,20 @@ public partial class MainWindow : Window
private async void ConnectButton_Click(object? sender, RoutedEventArgs e)
{
var normalizedBaseUrl = NormalizeBaseUrl(_settings.BaseUrl);
if (normalizedBaseUrl is null)
{
UpdateStatus("Ungültige Basis-URL. Bitte Support kontaktieren.");
SetLastError("Ungültige Basis-URL.");
return;
}
if (!string.Equals(normalizedBaseUrl, _settings.BaseUrl, StringComparison.OrdinalIgnoreCase))
{
_settings.BaseUrl = normalizedBaseUrl;
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
}
var code = (CodeBox.Text ?? string.Empty).Trim();
if (code.Length != 6 || code.Any(ch => ch is < '0' or > '9'))
@@ -54,18 +77,23 @@ public partial class MainWindow : Window
if (response.Data is null)
{
StatusText.Text = response.Message ?? "Verbindung fehlgeschlagen.";
SetLastError(response.Message ?? "Verbindung fehlgeschlagen.");
ConnectButton.IsEnabled = true;
return;
}
ClearLastError();
_settings.UploadUrl = ResolveUploadUrl(response.Data.UploadUrl);
_settings.Username = response.Data.Username;
_settings.Password = response.Data.Password;
_settings.ResponseFormat = response.Data.ResponseFormat;
_settings.EventName = response.Data.EventName;
_settingsStore.Save(_settings);
StatusText.Text = "Verbunden. Upload bereit.";
PickFolderButton.IsEnabled = true;
TestUploadButton.IsEnabled = true;
UpdateDiagnostics();
StartUploadPipelineIfReady();
ConnectButton.IsEnabled = true;
}
@@ -105,9 +133,12 @@ public partial class MainWindow : Window
{
StatusText.Text = "Verbunden. Upload bereit.";
PickFolderButton.IsEnabled = true;
TestUploadButton.IsEnabled = true;
StartUploadPipelineIfReady();
}
UpdateCountersText();
UpdateDiagnostics();
UpdateSteps();
}
@@ -173,29 +204,40 @@ public partial class MainWindow : Window
private void OnQueued(string path)
{
_queuedCount = Math.Max(0, _queuedCount + 1);
UpdateUpload(path, UploadStatus.Queued);
UpdateStatusIfAllowed($"Wartet: {Path.GetFileName(path)}", false);
UpdateCountersText();
}
private void OnUploading(string path)
{
_queuedCount = Math.Max(0, _queuedCount - 1);
_uploadingCount = Math.Max(0, _uploadingCount + 1);
UpdateUpload(path, UploadStatus.Uploading);
UpdateStatusIfAllowed($"Upload läuft: {Path.GetFileName(path)}", false);
UpdateCountersText();
}
private void OnSuccess(string path)
{
_failedPaths.Remove(path);
_uploadingCount = Math.Max(0, _uploadingCount - 1);
UpdateUpload(path, UploadStatus.Success);
UpdateStatusIfAllowed($"Hochgeladen: {Path.GetFileName(path)}", false);
UpdateCountersText();
}
private void OnFailure(string path)
{
_failedPaths.Add(path);
_uploadingCount = Math.Max(0, _uploadingCount - 1);
_failedCount = Math.Max(0, _failedCount + 1);
UpdateUpload(path, UploadStatus.Failed);
UpdateStatusIfAllowed($"Upload fehlgeschlagen: {Path.GetFileName(path)}", true);
SetLastError($"Upload fehlgeschlagen: {Path.GetFileName(path)}");
UpdateRetryButton();
UpdateCountersText();
}
private void UpdateUpload(string path, UploadStatus status)
@@ -244,13 +286,22 @@ public partial class MainWindow : Window
private void RetryFailedButton_Click(object? sender, RoutedEventArgs e)
{
if (_failedPaths.Count == 0)
{
return;
}
var retried = _failedPaths.Count;
foreach (var path in _failedPaths.ToList())
{
_uploadService.Enqueue(path, OnQueued);
}
_failedPaths.Clear();
_failedCount = Math.Max(0, _failedCount - retried);
_queuedCount = Math.Max(0, _queuedCount + retried);
UpdateRetryButton();
UpdateCountersText();
}
private void UpdateSteps()
@@ -264,6 +315,37 @@ public partial class MainWindow : Window
StepReadyText.Text = ready ? "3. Upload läuft ✓" : "3. Upload läuft";
}
private async void TestUploadButton_Click(object? sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(_settings.UploadUrl))
{
UpdateStatus("Bitte zuerst verbinden.");
return;
}
try
{
var tempPath = await CreateSampleUploadAsync();
_uploadService.Enqueue(tempPath, OnQueued);
UpdateStatusIfAllowed("Test-Upload hinzugefügt.", false);
}
catch
{
UpdateStatus("Test-Upload konnte nicht erstellt werden.");
SetLastError("Test-Upload konnte nicht erstellt werden.");
}
}
private async Task<string> CreateSampleUploadAsync()
{
var uri = new Uri("avares://PhotoboothUploader/Assets/sample-upload.png");
await using var source = AssetLoader.Open(uri);
var tempPath = Path.Combine(Path.GetTempPath(), $"fotospiel-test-{DateTimeOffset.Now:yyyyMMddHHmmss}.png");
await using var target = File.Create(tempPath);
await source.CopyToAsync(target);
return tempPath;
}
private string? ResolveUploadUrl(string? uploadUrl)
{
if (string.IsNullOrWhiteSpace(uploadUrl))
@@ -279,4 +361,82 @@ public partial class MainWindow : Window
var baseUri = new Uri(_settings.BaseUrl ?? DefaultBaseUrl, UriKind.Absolute);
return new Uri(baseUri, uploadUrl).ToString();
}
private static string? NormalizeBaseUrl(string? baseUrl)
{
if (string.IsNullOrWhiteSpace(baseUrl))
{
return null;
}
var trimmed = baseUrl.Trim();
if (Uri.TryCreate(trimmed, UriKind.Absolute, out var absolute))
{
return absolute.GetLeftPart(UriPartial.Authority);
}
if (Uri.TryCreate($"https://{trimmed}", UriKind.Absolute, out absolute))
{
return absolute.GetLeftPart(UriPartial.Authority);
}
return null;
}
private void UpdateCountersText()
{
Dispatcher.UIThread.Post(() =>
{
QueueStatusText.Text = $"Warteschlange: {_queuedCount} · Läuft: {_uploadingCount} · Fehlgeschlagen: {_failedCount}";
});
}
private void UpdateDiagnostics()
{
Dispatcher.UIThread.Post(() =>
{
EventNameText.Text = $"Event: {_settings.EventName ?? ""}";
BaseUrlText.Text = $"Basis-URL: {_settings.BaseUrl ?? ""}";
VersionText.Text = $"App-Version: {GetAppVersion()}";
LastErrorText.Text = $"Letzter Fehler: {FormatLastError()}";
});
}
private void SetLastError(string message)
{
_settings.LastError = message;
_settings.LastErrorAt = DateTimeOffset.Now.ToString("O");
_settingsStore.Save(_settings);
UpdateDiagnostics();
}
private void ClearLastError()
{
_settings.LastError = null;
_settings.LastErrorAt = null;
_settingsStore.Save(_settings);
UpdateDiagnostics();
}
private string FormatLastError()
{
if (string.IsNullOrWhiteSpace(_settings.LastError))
{
return "—";
}
if (DateTimeOffset.TryParse(_settings.LastErrorAt, out var timestamp))
{
return $"{timestamp:dd.MM.yyyy HH:mm} {_settings.LastError}";
}
return _settings.LastError;
}
private static string GetAppVersion()
{
var version = Assembly.GetExecutingAssembly().GetName().Version;
return version is null ? "0.0.0" : version.ToString();
}
}