Persist upload queue and uploaded cache
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-13 11:12:26 +01:00
parent ce0b7c951a
commit 0c33c1ddc1
2 changed files with 113 additions and 0 deletions

View File

@@ -40,6 +40,7 @@ public partial class MainWindow : Window
{ {
InitializeComponent(); InitializeComponent();
_settings = _settingsStore.Load(); _settings = _settingsStore.Load();
EnsureSettingsCollections();
_settings.BaseUrl = NormalizeBaseUrl(_settings.BaseUrl) ?? DefaultBaseUrl; _settings.BaseUrl = NormalizeBaseUrl(_settings.BaseUrl) ?? DefaultBaseUrl;
if (_settings.MaxConcurrentUploads <= 0) if (_settings.MaxConcurrentUploads <= 0)
{ {
@@ -226,6 +227,7 @@ public partial class MainWindow : Window
ResetCounters(); ResetCounters();
_uploadService.Start(_settings, OnQueued, OnUploading, OnSuccess, OnFailure); _uploadService.Start(_settings, OnQueued, OnUploading, OnSuccess, OnFailure);
StartWatcher(_settings.WatchFolder); StartWatcher(_settings.WatchFolder);
RestorePendingUploads();
UpdateSteps(); UpdateSteps();
} }
@@ -251,6 +253,11 @@ public partial class MainWindow : Window
return; return;
} }
if (ShouldSkipUpload(e.FullPath))
{
return;
}
_uploadService.Enqueue(e.FullPath, OnQueued); _uploadService.Enqueue(e.FullPath, OnQueued);
} }
@@ -261,6 +268,11 @@ public partial class MainWindow : Window
return; return;
} }
if (ShouldSkipUpload(e.FullPath))
{
return;
}
_uploadService.Enqueue(e.FullPath, OnQueued); _uploadService.Enqueue(e.FullPath, OnQueued);
} }
@@ -280,6 +292,7 @@ public partial class MainWindow : Window
{ {
Interlocked.Increment(ref _queuedCount); Interlocked.Increment(ref _queuedCount);
UpdateUpload(path, UploadStatus.Queued); UpdateUpload(path, UploadStatus.Queued);
AddPendingUpload(path);
UpdateStatusIfAllowed($"Wartet: {Path.GetFileName(path)}", false); UpdateStatusIfAllowed($"Wartet: {Path.GetFileName(path)}", false);
UpdateCountersText(); UpdateCountersText();
} }
@@ -299,6 +312,8 @@ public partial class MainWindow : Window
Interlocked.Decrement(ref _uploadingCount); Interlocked.Decrement(ref _uploadingCount);
_lastSuccessAt = DateTimeOffset.Now; _lastSuccessAt = DateTimeOffset.Now;
UpdateUpload(path, UploadStatus.Success); UpdateUpload(path, UploadStatus.Success);
RemovePendingUpload(path);
MarkUploaded(path);
UpdateStatusIfAllowed($"Hochgeladen: {Path.GetFileName(path)}", false); UpdateStatusIfAllowed($"Hochgeladen: {Path.GetFileName(path)}", false);
UpdateCountersText(); UpdateCountersText();
UpdateLiveStatus(); UpdateLiveStatus();
@@ -310,6 +325,7 @@ public partial class MainWindow : Window
Interlocked.Decrement(ref _uploadingCount); Interlocked.Decrement(ref _uploadingCount);
Interlocked.Increment(ref _failedCount); Interlocked.Increment(ref _failedCount);
UpdateUpload(path, UploadStatus.Failed); UpdateUpload(path, UploadStatus.Failed);
RemovePendingUpload(path);
UpdateStatusIfAllowed($"Upload fehlgeschlagen: {Path.GetFileName(path)}", true); UpdateStatusIfAllowed($"Upload fehlgeschlagen: {Path.GetFileName(path)}", true);
SetLastError($"{Path.GetFileName(path)} {message}"); SetLastError($"{Path.GetFileName(path)} {message}");
UpdateRetryButton(); UpdateRetryButton();
@@ -398,6 +414,92 @@ public partial class MainWindow : Window
ReconnectButton.IsEnabled = enabled; ReconnectButton.IsEnabled = enabled;
} }
private void RestorePendingUploads()
{
EnsureSettingsCollections();
if (_settings.PendingUploads.Count == 0)
{
return;
}
var pending = _settings.PendingUploads.ToList();
var changed = false;
foreach (var path in pending)
{
if (string.IsNullOrWhiteSpace(path) || !File.Exists(path) || ShouldSkipUpload(path))
{
_settings.PendingUploads.Remove(path);
changed = true;
continue;
}
_uploadService.Enqueue(path, OnQueued);
}
if (changed)
{
_settingsStore.Save(_settings);
}
}
private bool ShouldSkipUpload(string path)
{
var signature = GetUploadSignature(path);
if (signature is null)
{
return true;
}
return _settings.UploadedFiles.TryGetValue(path, out var recorded) && recorded == signature;
}
private string? GetUploadSignature(string path)
{
if (!File.Exists(path))
{
return null;
}
var info = new FileInfo(path);
return $"{info.Length}:{info.LastWriteTimeUtc.Ticks}";
}
private void AddPendingUpload(string path)
{
EnsureSettingsCollections();
if (!_settings.PendingUploads.Contains(path))
{
_settings.PendingUploads.Add(path);
_settingsStore.Save(_settings);
}
}
private void RemovePendingUpload(string path)
{
EnsureSettingsCollections();
if (_settings.PendingUploads.Remove(path))
{
_settingsStore.Save(_settings);
}
}
private void MarkUploaded(string path)
{
var signature = GetUploadSignature(path);
if (signature is null)
{
return;
}
EnsureSettingsCollections();
_settings.UploadedFiles[path] = signature;
_settingsStore.Save(_settings);
}
private void TitleText_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e) private void TitleText_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)
{ {
if (e.ClickCount < 2) if (e.ClickCount < 2)
@@ -706,4 +808,10 @@ public partial class MainWindow : Window
{ {
return code.Length == 6 && code.All(ch => ch is >= '0' and <= '9'); return code.Length == 6 && code.All(ch => ch is >= '0' and <= '9');
} }
private void EnsureSettingsCollections()
{
_settings.PendingUploads ??= new List<string>();
_settings.UploadedFiles ??= new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
} }

View File

@@ -1,3 +1,6 @@
using System;
using System.Collections.Generic;
namespace PhotoboothUploader.Models; namespace PhotoboothUploader.Models;
public sealed class PhotoboothSettings public sealed class PhotoboothSettings
@@ -9,6 +12,8 @@ public sealed class PhotoboothSettings
public string? Password { get; set; } public string? Password { get; set; }
public string? ResponseFormat { get; set; } public string? ResponseFormat { get; set; }
public string? WatchFolder { get; set; } public string? WatchFolder { get; set; }
public List<string> PendingUploads { get; set; } = new();
public Dictionary<string, string> UploadedFiles { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public string? LastError { get; set; } public string? LastError { get; set; }
public string? LastErrorAt { get; set; } public string? LastErrorAt { get; set; }
public int MaxConcurrentUploads { get; set; } = 2; public int MaxConcurrentUploads { get; set; } = 2;