1351 lines
39 KiB
C#
1351 lines
39 KiB
C#
using System;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Collections.Generic;
|
||
using System.Collections.ObjectModel;
|
||
using System.Reflection;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using System.Net.Http;
|
||
using System.Net.Http.Headers;
|
||
using Avalonia.Controls;
|
||
using Avalonia.Interactivity;
|
||
using Avalonia.Platform.Storage;
|
||
using Avalonia.Platform;
|
||
using Avalonia.Threading;
|
||
using PhotoboothUploader.Models;
|
||
using PhotoboothUploader.Services;
|
||
|
||
namespace PhotoboothUploader;
|
||
|
||
public partial class MainWindow : Window
|
||
{
|
||
private const string DefaultBaseUrl = "https://fotospiel.app";
|
||
private PhotoboothConnectClient _client;
|
||
private readonly SettingsStore _settingsStore = new();
|
||
private readonly UploadService _uploadService = new();
|
||
private PhotoboothSettings _settings;
|
||
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;
|
||
private DateTimeOffset? _lastSuccessAt;
|
||
private bool _advancedVisible;
|
||
private readonly DispatcherTimer _liveTimer = new();
|
||
private readonly List<string> _logBuffer = new();
|
||
|
||
public ObservableCollection<UploadItem> RecentUploads { get; } = new();
|
||
|
||
public MainWindow()
|
||
{
|
||
InitializeComponent();
|
||
_settings = _settingsStore.Load();
|
||
EnsureSettingsCollections();
|
||
_settings.BaseUrl = NormalizeBaseUrl(_settings.BaseUrl) ?? DefaultBaseUrl;
|
||
if (_settings.MaxConcurrentUploads <= 0)
|
||
{
|
||
_settings.MaxConcurrentUploads = 2;
|
||
}
|
||
_userAgent = $"FotospielPhotoboothUploader/{GetAppVersion()}";
|
||
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
|
||
_uploadService.Configure(_userAgent);
|
||
_settingsStore.Save(_settings);
|
||
DataContext = this;
|
||
_liveTimer.Interval = TimeSpan.FromSeconds(30);
|
||
_liveTimer.Tick += (_, _) => UpdateLiveStatus();
|
||
_liveTimer.Start();
|
||
Opened += OnWindowOpened;
|
||
Closing += OnWindowClosing;
|
||
ApplySettings();
|
||
}
|
||
|
||
private async void ConnectButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var code = (CodeBox.Text ?? string.Empty).Trim();
|
||
|
||
if (!IsValidCode(code))
|
||
{
|
||
StatusText.Text = "Bitte einen gültigen 6-stelligen Code eingeben.";
|
||
return;
|
||
}
|
||
|
||
ConnectButton.IsEnabled = false;
|
||
ReconnectButton.IsEnabled = false;
|
||
StatusText.Text = "Verbinde...";
|
||
|
||
await RedeemCodeAsync(code);
|
||
|
||
ConnectButton.IsEnabled = true;
|
||
ReconnectButton.IsEnabled = true;
|
||
}
|
||
|
||
private async void ReconnectButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var code = (CodeBox.Text ?? string.Empty).Trim();
|
||
|
||
if (!IsValidCode(code))
|
||
{
|
||
StatusText.Text = "Bitte einen gültigen 6-stelligen Code eingeben.";
|
||
return;
|
||
}
|
||
|
||
ReconnectButton.IsEnabled = false;
|
||
StatusText.Text = "Verbindung erneuern...";
|
||
|
||
await RedeemCodeAsync(code);
|
||
|
||
ReconnectButton.IsEnabled = true;
|
||
}
|
||
|
||
private async Task RedeemCodeAsync(string code)
|
||
{
|
||
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 response = await _client.RedeemAsync(code);
|
||
|
||
if (response.Data is null)
|
||
{
|
||
StatusText.Text = response.Message ?? "Verbindung fehlgeschlagen.";
|
||
SetLastError(response.Message ?? "Verbindung fehlgeschlagen.");
|
||
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;
|
||
_settings.ConnectExpiresAt = response.Data.ExpiresAt;
|
||
_settingsStore.Save(_settings);
|
||
|
||
StatusText.Text = "Verbunden. Upload bereit.";
|
||
AppendLog("Verbunden mit Event.");
|
||
PickFolderButton.IsEnabled = true;
|
||
TestUploadButton.IsEnabled = true;
|
||
ReconnectButton.IsEnabled = true;
|
||
UpdateAdvancedLockState();
|
||
UpdateDiagnostics();
|
||
StartUploadPipelineIfReady();
|
||
}
|
||
|
||
private async void PickFolderButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var options = new FolderPickerOpenOptions
|
||
{
|
||
Title = "Upload-Ordner auswählen",
|
||
AllowMultiple = false,
|
||
};
|
||
|
||
var folders = await StorageProvider.OpenFolderPickerAsync(options);
|
||
var folder = folders.FirstOrDefault();
|
||
var localPath = folder?.TryGetLocalPath();
|
||
|
||
if (string.IsNullOrWhiteSpace(localPath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
_settings.WatchFolder = localPath;
|
||
_settingsStore.Save(_settings);
|
||
|
||
FolderText.Text = localPath;
|
||
UpdateFolderHealth();
|
||
StartUploadPipelineIfReady();
|
||
}
|
||
|
||
private void DslrBoothPresetButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
ApplyPresetFolder(GetDslrBoothFolder());
|
||
}
|
||
|
||
private void SparkboothPresetButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
ApplyPresetFolder(GetSparkboothFolder());
|
||
}
|
||
|
||
private void ApplySettings()
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(_settings.WatchFolder))
|
||
{
|
||
FolderText.Text = _settings.WatchFolder;
|
||
}
|
||
|
||
BaseUrlBox.Text = _settings.BaseUrl ?? DefaultBaseUrl;
|
||
MaxUploadsBox.Text = _settings.MaxConcurrentUploads.ToString();
|
||
UploadTempoBox.SelectedIndex = ResolveUploadTempoIndex(_settings.UploadDelayMs);
|
||
IncludePatternsBox.Text = _settings.IncludePatterns ?? string.Empty;
|
||
ExcludePatternsBox.Text = _settings.ExcludePatterns ?? string.Empty;
|
||
ResponseFormatBox.SelectedIndex = ResolveResponseFormatIndex(_settings.ResponseFormat);
|
||
ManualUploadUrlBox.Text = _settings.UploadUrl ?? string.Empty;
|
||
ManualUsernameBox.Text = _settings.Username ?? string.Empty;
|
||
ManualPasswordBox.Text = string.Empty;
|
||
SettingsUnlockToggle.IsChecked = false;
|
||
|
||
if (!string.IsNullOrWhiteSpace(_settings.UploadUrl))
|
||
{
|
||
StatusText.Text = "Verbunden. Upload bereit.";
|
||
PickFolderButton.IsEnabled = true;
|
||
TestUploadButton.IsEnabled = true;
|
||
ReconnectButton.IsEnabled = true;
|
||
StartUploadPipelineIfReady();
|
||
}
|
||
|
||
UpdateCountersText();
|
||
UpdateFolderHealth();
|
||
UpdateDiagnostics();
|
||
UpdateSteps();
|
||
RefreshProfiles();
|
||
UpdateAdvancedLockState();
|
||
UpdatePresetButtons();
|
||
}
|
||
|
||
private void OnWindowOpened(object? sender, EventArgs e)
|
||
{
|
||
ApplyWindowSize();
|
||
}
|
||
|
||
private void OnWindowClosing(object? sender, WindowClosingEventArgs e)
|
||
{
|
||
_settings.WindowWidth = Width;
|
||
_settings.WindowHeight = Height;
|
||
_settingsStore.Save(_settings);
|
||
}
|
||
|
||
private void ApplyWindowSize()
|
||
{
|
||
if (_settings.WindowWidth > 0)
|
||
{
|
||
Width = Math.Max(MinWidth, _settings.WindowWidth);
|
||
}
|
||
|
||
if (_settings.WindowHeight > 0)
|
||
{
|
||
Height = Math.Max(MinHeight, _settings.WindowHeight);
|
||
}
|
||
}
|
||
|
||
private void StartUploadPipelineIfReady()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(_settings.UploadUrl) || string.IsNullOrWhiteSpace(_settings.WatchFolder))
|
||
{
|
||
UpdateSteps();
|
||
return;
|
||
}
|
||
|
||
ResetCounters();
|
||
_uploadService.Start(_settings, OnQueued, OnUploading, OnSuccess, OnFailure);
|
||
StartWatcher(_settings.WatchFolder);
|
||
RestorePendingUploads();
|
||
UpdateSteps();
|
||
}
|
||
|
||
private void StartWatcher(string folder)
|
||
{
|
||
_watcher?.Dispose();
|
||
|
||
_watcher = new FileSystemWatcher(folder)
|
||
{
|
||
IncludeSubdirectories = false,
|
||
EnableRaisingEvents = true,
|
||
};
|
||
|
||
_watcher.Created += OnFileChanged;
|
||
_watcher.Changed += OnFileChanged;
|
||
_watcher.Renamed += OnFileRenamed;
|
||
}
|
||
|
||
private void OnFileChanged(object sender, FileSystemEventArgs e)
|
||
{
|
||
if (!IsSupportedImage(e.FullPath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (ShouldSkipUpload(e.FullPath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
RecordLastSeen(e.FullPath);
|
||
_uploadService.Enqueue(e.FullPath, OnQueued);
|
||
}
|
||
|
||
private void OnFileRenamed(object sender, RenamedEventArgs e)
|
||
{
|
||
if (!IsSupportedImage(e.FullPath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (ShouldSkipUpload(e.FullPath))
|
||
{
|
||
return;
|
||
}
|
||
|
||
RecordLastSeen(e.FullPath);
|
||
_uploadService.Enqueue(e.FullPath, OnQueued);
|
||
}
|
||
|
||
private bool IsSupportedImage(string path)
|
||
{
|
||
var extension = Path.GetExtension(path)?.ToLowerInvariant();
|
||
|
||
return extension is ".jpg" or ".jpeg" or ".png" or ".webp";
|
||
}
|
||
|
||
private void UpdateStatus(string message)
|
||
{
|
||
Dispatcher.UIThread.Post(() => StatusText.Text = message);
|
||
}
|
||
|
||
private void OnQueued(string path)
|
||
{
|
||
Interlocked.Increment(ref _queuedCount);
|
||
UpdateUpload(path, UploadStatus.Queued);
|
||
AddPendingUpload(path);
|
||
UpdateStatusIfAllowed($"Wartet: {Path.GetFileName(path)}", false);
|
||
AppendLog($"Wartet: {Path.GetFileName(path)}");
|
||
UpdateCountersText();
|
||
}
|
||
|
||
private void OnUploading(string path)
|
||
{
|
||
Interlocked.Decrement(ref _queuedCount);
|
||
Interlocked.Increment(ref _uploadingCount);
|
||
UpdateUpload(path, UploadStatus.Uploading);
|
||
UpdateStatusIfAllowed($"Upload läuft: {Path.GetFileName(path)}", false);
|
||
UpdateCountersText();
|
||
}
|
||
|
||
private void OnSuccess(string path)
|
||
{
|
||
_failedPaths.Remove(path);
|
||
Interlocked.Decrement(ref _uploadingCount);
|
||
_lastSuccessAt = DateTimeOffset.Now;
|
||
UpdateUpload(path, UploadStatus.Success);
|
||
RemovePendingUpload(path);
|
||
MarkUploaded(path);
|
||
UpdateStatusIfAllowed($"Hochgeladen: {Path.GetFileName(path)}", false);
|
||
AppendLog($"Hochgeladen: {Path.GetFileName(path)}");
|
||
UpdateCountersText();
|
||
UpdateLiveStatus();
|
||
}
|
||
|
||
private void OnFailure(string path, string message)
|
||
{
|
||
_failedPaths.Add(path);
|
||
Interlocked.Decrement(ref _uploadingCount);
|
||
Interlocked.Increment(ref _failedCount);
|
||
UpdateUpload(path, UploadStatus.Failed);
|
||
RemovePendingUpload(path);
|
||
UpdateStatusIfAllowed($"Upload fehlgeschlagen: {Path.GetFileName(path)}", true);
|
||
SetLastError($"{Path.GetFileName(path)} – {message}");
|
||
AppendLog($"Fehlgeschlagen: {Path.GetFileName(path)} – {message}");
|
||
UpdateRetryButton();
|
||
UpdateCountersText();
|
||
}
|
||
|
||
private void UpdateUpload(string path, UploadStatus status)
|
||
{
|
||
Dispatcher.UIThread.Post(() =>
|
||
{
|
||
if (!_uploadsByPath.TryGetValue(path, out var item))
|
||
{
|
||
item = new UploadItem(path);
|
||
_uploadsByPath[path] = item;
|
||
RecentUploads.Insert(0, item);
|
||
}
|
||
|
||
item.Status = status;
|
||
LastUploadText.Text = status == UploadStatus.Success
|
||
? $"Letzter Upload: {item.UpdatedLabel}"
|
||
: LastUploadText.Text;
|
||
|
||
while (RecentUploads.Count > 3)
|
||
{
|
||
var last = RecentUploads[^1];
|
||
_uploadsByPath.Remove(last.Path);
|
||
RecentUploads.RemoveAt(RecentUploads.Count - 1);
|
||
}
|
||
|
||
UpdateRetryButton();
|
||
});
|
||
}
|
||
|
||
private void UpdateStatusIfAllowed(string message, bool important)
|
||
{
|
||
var quiet = QuietToggle?.IsChecked ?? false;
|
||
|
||
if (quiet && !important)
|
||
{
|
||
return;
|
||
}
|
||
|
||
UpdateStatus(message);
|
||
}
|
||
|
||
private void UpdateRetryButton()
|
||
{
|
||
RetryFailedButton.IsEnabled = _failedPaths.Count > 0;
|
||
ClearFailedButton.IsEnabled = _failedPaths.Count > 0;
|
||
}
|
||
|
||
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();
|
||
Interlocked.Add(ref _failedCount, -retried);
|
||
Interlocked.Add(ref _queuedCount, retried);
|
||
UpdateRetryButton();
|
||
UpdateCountersText();
|
||
}
|
||
|
||
private void ClearFailedButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
if (_failedPaths.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var cleared = _failedPaths.Count;
|
||
_failedPaths.Clear();
|
||
Interlocked.Add(ref _failedCount, -cleared);
|
||
UpdateRetryButton();
|
||
UpdateCountersText();
|
||
UpdateStatusIfAllowed("Fehlerliste geleert.", false);
|
||
}
|
||
|
||
private void UpdateSteps()
|
||
{
|
||
var hasCode = !string.IsNullOrWhiteSpace(_settings.UploadUrl);
|
||
var hasFolder = !string.IsNullOrWhiteSpace(_settings.WatchFolder);
|
||
var ready = hasCode && hasFolder;
|
||
|
||
StepCodeText.Text = hasCode ? "1. Code eingeben ✓" : "1. Code eingeben";
|
||
StepFolderText.Text = hasFolder ? "2. Upload-Ordner wählen ✓" : "2. Upload-Ordner wählen";
|
||
StepReadyText.Text = ready ? "3. Upload läuft ✓" : "3. Upload läuft";
|
||
}
|
||
|
||
private void CodeBox_TextChanged(object? sender, TextChangedEventArgs e)
|
||
{
|
||
var code = (CodeBox.Text ?? string.Empty).Trim();
|
||
var enabled = IsValidCode(code);
|
||
ReconnectButton.IsEnabled = enabled;
|
||
}
|
||
|
||
private void SettingsUnlockToggle_Changed(object? sender, RoutedEventArgs e)
|
||
{
|
||
UpdateAdvancedLockState();
|
||
}
|
||
|
||
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 fileName = Path.GetFileName(path);
|
||
if (!MatchesIncludePatterns(fileName))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
if (MatchesExcludePatterns(fileName))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
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)
|
||
{
|
||
if (e.ClickCount < 2)
|
||
{
|
||
return;
|
||
}
|
||
|
||
_advancedVisible = !_advancedVisible;
|
||
AdvancedPanel.IsVisible = _advancedVisible;
|
||
}
|
||
|
||
private void SaveAdvancedButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var normalizedBaseUrl = NormalizeBaseUrl(BaseUrlBox.Text);
|
||
if (normalizedBaseUrl is null)
|
||
{
|
||
UpdateStatus("Ungültige Basis-URL.");
|
||
SetLastError("Ungültige Basis-URL.");
|
||
return;
|
||
}
|
||
|
||
if (!int.TryParse(MaxUploadsBox.Text, out var maxUploads) || maxUploads < 1 || maxUploads > 5)
|
||
{
|
||
UpdateStatus("Max. parallele Uploads muss zwischen 1 und 5 liegen.");
|
||
SetLastError("Ungültige Parallel-Uploads.");
|
||
return;
|
||
}
|
||
|
||
var manualUploadUrl = (ManualUploadUrlBox.Text ?? string.Empty).Trim();
|
||
var manualUsername = (ManualUsernameBox.Text ?? string.Empty).Trim();
|
||
var manualPassword = (ManualPasswordBox.Text ?? string.Empty).Trim();
|
||
|
||
_settings.BaseUrl = normalizedBaseUrl;
|
||
_settings.MaxConcurrentUploads = maxUploads;
|
||
_settings.UploadDelayMs = ResolveUploadDelay(UploadTempoBox.SelectedIndex);
|
||
_settings.IncludePatterns = NormalizePatternInput(IncludePatternsBox.Text);
|
||
_settings.ExcludePatterns = NormalizePatternInput(ExcludePatternsBox.Text);
|
||
_settings.ResponseFormat = ResolveResponseFormat(ResponseFormatBox.SelectedIndex);
|
||
|
||
if (!string.IsNullOrWhiteSpace(manualUploadUrl))
|
||
{
|
||
_settings.UploadUrl = ResolveUploadUrl(manualUploadUrl);
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(manualUsername))
|
||
{
|
||
_settings.Username = manualUsername;
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(manualPassword))
|
||
{
|
||
_settings.Password = manualPassword;
|
||
}
|
||
|
||
_settingsStore.Save(_settings);
|
||
|
||
_client = new PhotoboothConnectClient(_settings.BaseUrl, _userAgent);
|
||
_uploadService.Configure(_userAgent);
|
||
UpdateDiagnostics();
|
||
UpdateFolderHealth();
|
||
RestartUploadPipeline();
|
||
UpdateSteps();
|
||
UpdateAdvancedLockState();
|
||
|
||
if (!string.IsNullOrWhiteSpace(_settings.UploadUrl))
|
||
{
|
||
StatusText.Text = "Verbunden. Upload bereit.";
|
||
PickFolderButton.IsEnabled = true;
|
||
TestUploadButton.IsEnabled = true;
|
||
ReconnectButton.IsEnabled = true;
|
||
}
|
||
UpdateStatus("Einstellungen gespeichert.");
|
||
}
|
||
|
||
private async void TestConnectionButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var targetUrl = ResolveTestUrl();
|
||
if (targetUrl is null)
|
||
{
|
||
UpdateStatus("Bitte eine gültige Upload-URL oder Basis-URL speichern.");
|
||
return;
|
||
}
|
||
|
||
UpdateStatus("Verbindung wird getestet...");
|
||
|
||
try
|
||
{
|
||
using var client = new HttpClient
|
||
{
|
||
Timeout = TimeSpan.FromSeconds(8),
|
||
};
|
||
client.DefaultRequestHeaders.UserAgent.ParseAdd(_userAgent);
|
||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||
using var request = new HttpRequestMessage(HttpMethod.Head, targetUrl);
|
||
using var response = await client.SendAsync(request);
|
||
var status = $"{(int)response.StatusCode} {response.ReasonPhrase}".Trim();
|
||
UpdateStatus($"Server erreichbar ({status}).");
|
||
}
|
||
catch (TaskCanceledException)
|
||
{
|
||
UpdateStatus("Verbindungstest: Zeitüberschreitung.");
|
||
}
|
||
catch (HttpRequestException)
|
||
{
|
||
UpdateStatus("Verbindungstest: Netzwerkfehler.");
|
||
}
|
||
}
|
||
|
||
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 void ResetCounters()
|
||
{
|
||
Interlocked.Exchange(ref _queuedCount, 0);
|
||
Interlocked.Exchange(ref _uploadingCount, 0);
|
||
Interlocked.Exchange(ref _failedCount, 0);
|
||
_failedPaths.Clear();
|
||
_lastSuccessAt = null;
|
||
UpdateCountersText();
|
||
UpdateRetryButton();
|
||
UpdateLiveStatus();
|
||
}
|
||
|
||
private void RestartUploadPipeline()
|
||
{
|
||
_uploadService.Stop();
|
||
StartUploadPipelineIfReady();
|
||
}
|
||
|
||
private void UpdateFolderHealth()
|
||
{
|
||
var folder = _settings.WatchFolder;
|
||
if (string.IsNullOrWhiteSpace(folder))
|
||
{
|
||
FolderHealthText.Text = "Ordner: —";
|
||
DiskFreeText.Text = "Freier Speicher: —";
|
||
LastSeenText.Text = "Letzte Datei: —";
|
||
return;
|
||
}
|
||
|
||
if (!Directory.Exists(folder))
|
||
{
|
||
FolderHealthText.Text = "Ordner: fehlt";
|
||
DiskFreeText.Text = "Freier Speicher: —";
|
||
LastSeenText.Text = "Letzte Datei: —";
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
_ = Directory.EnumerateFileSystemEntries(folder).FirstOrDefault();
|
||
FolderHealthText.Text = CanWriteToFolder(folder) ? "Ordner: OK (schreibbar)" : "Ordner: OK (nur lesen)";
|
||
}
|
||
catch (UnauthorizedAccessException)
|
||
{
|
||
FolderHealthText.Text = "Ordner: Keine Berechtigung";
|
||
}
|
||
catch
|
||
{
|
||
FolderHealthText.Text = "Ordner: Fehler";
|
||
}
|
||
|
||
DiskFreeText.Text = FormatDiskFree(folder);
|
||
LastSeenText.Text = FormatLastSeen();
|
||
}
|
||
|
||
private void UpdateLiveStatus()
|
||
{
|
||
Dispatcher.UIThread.Post(() =>
|
||
{
|
||
if (_lastSuccessAt is null)
|
||
{
|
||
LiveStatusText.Text = "Live: —";
|
||
UpdateDiagnostics();
|
||
return;
|
||
}
|
||
|
||
var age = DateTimeOffset.Now - _lastSuccessAt.Value;
|
||
var isLive = age <= TimeSpan.FromMinutes(5);
|
||
var label = isLive ? "Live: Ja" : "Live: Nein";
|
||
LiveStatusText.Text = $"{label} (letzter Upload {FormatRelativeAge(age)})";
|
||
UpdateDiagnostics();
|
||
});
|
||
}
|
||
|
||
private static string FormatRelativeAge(TimeSpan age)
|
||
{
|
||
if (age.TotalMinutes < 1)
|
||
{
|
||
return "gerade eben";
|
||
}
|
||
|
||
if (age.TotalHours < 1)
|
||
{
|
||
return $"vor {Math.Round(age.TotalMinutes)} min";
|
||
}
|
||
|
||
if (age.TotalDays < 1)
|
||
{
|
||
return $"vor {Math.Round(age.TotalHours)} h";
|
||
}
|
||
|
||
return $"vor {Math.Round(age.TotalDays)} d";
|
||
}
|
||
|
||
private static string FormatDiskFree(string folder)
|
||
{
|
||
var root = Path.GetPathRoot(folder);
|
||
if (string.IsNullOrWhiteSpace(root))
|
||
{
|
||
return "Freier Speicher: —";
|
||
}
|
||
|
||
var drive = new DriveInfo(root);
|
||
if (!drive.IsReady)
|
||
{
|
||
return "Freier Speicher: —";
|
||
}
|
||
|
||
var freeGb = drive.AvailableFreeSpace / (1024d * 1024d * 1024d);
|
||
var label = freeGb < 5 ? "niedrig" : "ok";
|
||
return $"Freier Speicher: {freeGb:0.0} GB ({label})";
|
||
}
|
||
|
||
private string? ResolveUploadUrl(string? uploadUrl)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(uploadUrl))
|
||
{
|
||
return uploadUrl;
|
||
}
|
||
|
||
if (Uri.TryCreate(uploadUrl, UriKind.Absolute, out _))
|
||
{
|
||
return uploadUrl;
|
||
}
|
||
|
||
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(() =>
|
||
{
|
||
var queued = Volatile.Read(ref _queuedCount);
|
||
var uploading = Volatile.Read(ref _uploadingCount);
|
||
var failed = Volatile.Read(ref _failedCount);
|
||
QueueStatusText.Text = $"Warteschlange: {Math.Max(0, queued)} · Läuft: {Math.Max(0, uploading)} · Fehlgeschlagen: {Math.Max(0, failed)}";
|
||
});
|
||
}
|
||
|
||
private void UpdateDiagnostics()
|
||
{
|
||
Dispatcher.UIThread.Post(() =>
|
||
{
|
||
EventNameText.Text = $"Event: {_settings.EventName ?? "—"}";
|
||
BaseUrlText.Text = $"Basis-URL: {_settings.BaseUrl ?? "—"}";
|
||
VersionText.Text = $"App-Version: {GetAppVersion()}";
|
||
ConnectExpiryText.Text = FormatConnectExpiry();
|
||
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();
|
||
}
|
||
|
||
private static bool IsValidCode(string code)
|
||
{
|
||
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);
|
||
_settings.Profiles ??= new List<PhotoboothProfile>();
|
||
}
|
||
|
||
private void RecordLastSeen(string path)
|
||
{
|
||
_settings.LastSeenFile = Path.GetFileName(path);
|
||
_settings.LastSeenAt = DateTimeOffset.Now.ToString("O");
|
||
_settingsStore.Save(_settings);
|
||
UpdateFolderHealth();
|
||
}
|
||
|
||
private bool CanWriteToFolder(string folder)
|
||
{
|
||
try
|
||
{
|
||
var testFile = Path.Combine(folder, $".fotospiel-write-test-{Guid.NewGuid():N}.tmp");
|
||
File.WriteAllText(testFile, "ok");
|
||
File.Delete(testFile);
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private string FormatLastSeen()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(_settings.LastSeenAt) || string.IsNullOrWhiteSpace(_settings.LastSeenFile))
|
||
{
|
||
return "Letzte Datei: —";
|
||
}
|
||
|
||
if (DateTimeOffset.TryParse(_settings.LastSeenAt, out var timestamp))
|
||
{
|
||
return $"Letzte Datei: {_settings.LastSeenFile} ({timestamp:HH:mm})";
|
||
}
|
||
|
||
return $"Letzte Datei: {_settings.LastSeenFile}";
|
||
}
|
||
|
||
private string FormatConnectExpiry()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(_settings.ConnectExpiresAt))
|
||
{
|
||
return "Verbindungscode: —";
|
||
}
|
||
|
||
if (!DateTimeOffset.TryParse(_settings.ConnectExpiresAt, out var expiry))
|
||
{
|
||
return "Verbindungscode: —";
|
||
}
|
||
|
||
var remaining = expiry - DateTimeOffset.Now;
|
||
if (remaining <= TimeSpan.Zero)
|
||
{
|
||
return "Verbindungscode: abgelaufen";
|
||
}
|
||
|
||
if (remaining.TotalHours >= 1)
|
||
{
|
||
return $"Verbindungscode: {Math.Ceiling(remaining.TotalHours)} h gueltig";
|
||
}
|
||
|
||
return $"Verbindungscode: {Math.Ceiling(remaining.TotalMinutes)} min gueltig";
|
||
}
|
||
|
||
private string? ResolveTestUrl()
|
||
{
|
||
var manual = NormalizeUrl(ManualUploadUrlBox.Text);
|
||
if (!string.IsNullOrWhiteSpace(manual))
|
||
{
|
||
return manual;
|
||
}
|
||
|
||
if (!string.IsNullOrWhiteSpace(_settings.UploadUrl))
|
||
{
|
||
return _settings.UploadUrl;
|
||
}
|
||
|
||
var normalizedBase = NormalizeBaseUrl(BaseUrlBox.Text);
|
||
return normalizedBase;
|
||
}
|
||
|
||
private static string? NormalizeUrl(string? value)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var trimmed = value.Trim();
|
||
if (Uri.TryCreate(trimmed, UriKind.Absolute, out _))
|
||
{
|
||
return trimmed;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private static int ResolveUploadDelay(int selectedIndex)
|
||
{
|
||
return selectedIndex switch
|
||
{
|
||
0 => 0,
|
||
2 => 1500,
|
||
_ => 500,
|
||
};
|
||
}
|
||
|
||
private static int ResolveUploadTempoIndex(int delay)
|
||
{
|
||
if (delay <= 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
if (delay >= 1500)
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
private static string? ResolveResponseFormat(int selectedIndex)
|
||
{
|
||
return selectedIndex switch
|
||
{
|
||
1 => "json",
|
||
2 => "xml",
|
||
_ => null,
|
||
};
|
||
}
|
||
|
||
private static int ResolveResponseFormatIndex(string? format)
|
||
{
|
||
if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
if (string.Equals(format, "xml", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
return 2;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
private static string NormalizePatternInput(string? input)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(input))
|
||
{
|
||
return string.Empty;
|
||
}
|
||
|
||
var parts = SplitPatterns(input);
|
||
return string.Join(';', parts);
|
||
}
|
||
|
||
private bool MatchesIncludePatterns(string fileName)
|
||
{
|
||
var patterns = SplitPatterns(_settings.IncludePatterns);
|
||
if (patterns.Count == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return patterns.Any(pattern => IsPatternMatch(fileName, pattern));
|
||
}
|
||
|
||
private bool MatchesExcludePatterns(string fileName)
|
||
{
|
||
var patterns = SplitPatterns(_settings.ExcludePatterns);
|
||
if (patterns.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return patterns.Any(pattern => IsPatternMatch(fileName, pattern));
|
||
}
|
||
|
||
private static List<string> SplitPatterns(string? input)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(input))
|
||
{
|
||
return new List<string>();
|
||
}
|
||
|
||
return input
|
||
.Split(new[] { ';', ',', '\n' }, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
|
||
.Where(pattern => pattern.Length > 0)
|
||
.ToList();
|
||
}
|
||
|
||
private static bool IsPatternMatch(string fileName, string pattern)
|
||
{
|
||
var span = fileName.AsSpan();
|
||
var token = pattern.AsSpan();
|
||
return IsPatternMatch(span, token);
|
||
}
|
||
|
||
private static bool IsPatternMatch(ReadOnlySpan<char> text, ReadOnlySpan<char> pattern)
|
||
{
|
||
var textIndex = 0;
|
||
var patternIndex = 0;
|
||
var starIndex = -1;
|
||
var matchIndex = 0;
|
||
|
||
while (textIndex < text.Length)
|
||
{
|
||
if (patternIndex < pattern.Length
|
||
&& (pattern[patternIndex] == '?' || char.ToLowerInvariant(pattern[patternIndex]) == char.ToLowerInvariant(text[textIndex])))
|
||
{
|
||
textIndex++;
|
||
patternIndex++;
|
||
continue;
|
||
}
|
||
|
||
if (patternIndex < pattern.Length && pattern[patternIndex] == '*')
|
||
{
|
||
starIndex = patternIndex;
|
||
matchIndex = textIndex;
|
||
patternIndex++;
|
||
continue;
|
||
}
|
||
|
||
if (starIndex != -1)
|
||
{
|
||
patternIndex = starIndex + 1;
|
||
matchIndex++;
|
||
textIndex = matchIndex;
|
||
continue;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
while (patternIndex < pattern.Length && pattern[patternIndex] == '*')
|
||
{
|
||
patternIndex++;
|
||
}
|
||
|
||
return patternIndex == pattern.Length;
|
||
}
|
||
|
||
private void UpdateAdvancedLockState()
|
||
{
|
||
var connected = !string.IsNullOrWhiteSpace(_settings.UploadUrl);
|
||
var unlocked = SettingsUnlockToggle?.IsChecked ?? false;
|
||
var enabled = !connected || unlocked;
|
||
|
||
BaseUrlBox.IsEnabled = enabled;
|
||
MaxUploadsBox.IsEnabled = enabled;
|
||
UploadTempoBox.IsEnabled = enabled;
|
||
IncludePatternsBox.IsEnabled = enabled;
|
||
ExcludePatternsBox.IsEnabled = enabled;
|
||
ResponseFormatBox.IsEnabled = enabled;
|
||
ManualUploadUrlBox.IsEnabled = enabled;
|
||
ManualUsernameBox.IsEnabled = enabled;
|
||
ManualPasswordBox.IsEnabled = enabled;
|
||
TestConnectionButton.IsEnabled = enabled;
|
||
SaveAdvancedButton.IsEnabled = enabled;
|
||
}
|
||
|
||
private void RefreshProfiles()
|
||
{
|
||
ProfilesBox.ItemsSource = _settings.Profiles.Select(profile => profile.DisplayName).ToList();
|
||
}
|
||
|
||
private void LoadProfileButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var index = ProfilesBox.SelectedIndex;
|
||
if (index < 0 || index >= _settings.Profiles.Count)
|
||
{
|
||
UpdateStatus("Bitte zuerst ein Profil auswaehlen.");
|
||
return;
|
||
}
|
||
|
||
var profile = _settings.Profiles[index];
|
||
_settings.BaseUrl = NormalizeBaseUrl(profile.BaseUrl) ?? _settings.BaseUrl;
|
||
_settings.UploadUrl = profile.UploadUrl;
|
||
_settings.Username = profile.Username;
|
||
_settings.Password = profile.Password;
|
||
_settings.ResponseFormat = profile.ResponseFormat;
|
||
_settings.WatchFolder = profile.WatchFolder;
|
||
_settings.IncludePatterns = profile.IncludePatterns;
|
||
_settings.ExcludePatterns = profile.ExcludePatterns;
|
||
_settings.MaxConcurrentUploads = profile.MaxConcurrentUploads > 0 ? profile.MaxConcurrentUploads : _settings.MaxConcurrentUploads;
|
||
_settings.UploadDelayMs = profile.UploadDelayMs;
|
||
_settingsStore.Save(_settings);
|
||
|
||
ApplySettings();
|
||
UpdateStatus("Profil geladen.");
|
||
}
|
||
|
||
private void SaveProfileButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var label = _settings.EventName ?? "Neues Profil";
|
||
var profile = new PhotoboothProfile
|
||
{
|
||
Label = label,
|
||
EventName = _settings.EventName,
|
||
BaseUrl = _settings.BaseUrl,
|
||
UploadUrl = _settings.UploadUrl,
|
||
Username = _settings.Username,
|
||
Password = _settings.Password,
|
||
ResponseFormat = _settings.ResponseFormat,
|
||
WatchFolder = _settings.WatchFolder,
|
||
IncludePatterns = _settings.IncludePatterns,
|
||
ExcludePatterns = _settings.ExcludePatterns,
|
||
MaxConcurrentUploads = _settings.MaxConcurrentUploads,
|
||
UploadDelayMs = _settings.UploadDelayMs,
|
||
};
|
||
|
||
_settings.Profiles.RemoveAll(existing => string.Equals(existing.DisplayName, profile.DisplayName, StringComparison.OrdinalIgnoreCase));
|
||
_settings.Profiles.Insert(0, profile);
|
||
_settingsStore.Save(_settings);
|
||
RefreshProfiles();
|
||
ProfilesBox.SelectedIndex = 0;
|
||
UpdateStatus("Profil gespeichert.");
|
||
}
|
||
|
||
private async void LogCopyButton_Click(object? sender, RoutedEventArgs e)
|
||
{
|
||
var content = ReadLogForCopy();
|
||
if (string.IsNullOrWhiteSpace(content))
|
||
{
|
||
UpdateStatus("Log ist leer.");
|
||
return;
|
||
}
|
||
|
||
var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
|
||
if (clipboard is null)
|
||
{
|
||
UpdateStatus("Zwischenablage nicht verfuegbar.");
|
||
return;
|
||
}
|
||
|
||
await clipboard.SetTextAsync(content);
|
||
UpdateStatus("Log kopiert.");
|
||
}
|
||
|
||
private void AppendLog(string message)
|
||
{
|
||
var line = $"{DateTimeOffset.Now:yyyy-MM-dd HH:mm:ss} {message}";
|
||
_logBuffer.Add(line);
|
||
while (_logBuffer.Count > 200)
|
||
{
|
||
_logBuffer.RemoveAt(0);
|
||
}
|
||
|
||
try
|
||
{
|
||
File.AppendAllLines(_settingsStore.LogPath, new[] { line });
|
||
}
|
||
catch
|
||
{
|
||
// ignore file errors
|
||
}
|
||
}
|
||
|
||
private string ReadLogForCopy()
|
||
{
|
||
try
|
||
{
|
||
if (File.Exists(_settingsStore.LogPath))
|
||
{
|
||
var lines = File.ReadAllLines(_settingsStore.LogPath);
|
||
return string.Join(Environment.NewLine, lines.TakeLast(200));
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// ignore
|
||
}
|
||
|
||
return _logBuffer.Count > 0 ? string.Join(Environment.NewLine, _logBuffer) : string.Empty;
|
||
}
|
||
|
||
private void UpdatePresetButtons()
|
||
{
|
||
DslrBoothPresetButton.IsVisible = OperatingSystem.IsWindows();
|
||
SparkboothPresetButton.IsVisible = OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();
|
||
}
|
||
|
||
private void ApplyPresetFolder(string? folder)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(folder))
|
||
{
|
||
UpdateStatus("Preset-Ordner nicht verfügbar.");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
Directory.CreateDirectory(folder);
|
||
}
|
||
catch
|
||
{
|
||
UpdateStatus("Preset-Ordner konnte nicht erstellt werden.");
|
||
return;
|
||
}
|
||
|
||
_settings.WatchFolder = folder;
|
||
_settingsStore.Save(_settings);
|
||
FolderText.Text = folder;
|
||
UpdateFolderHealth();
|
||
StartUploadPipelineIfReady();
|
||
}
|
||
|
||
private static string? GetDslrBoothFolder()
|
||
{
|
||
return OperatingSystem.IsWindows() ? @"C:\dslrBooth" : null;
|
||
}
|
||
|
||
private static string? GetSparkboothFolder()
|
||
{
|
||
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
|
||
if (string.IsNullOrWhiteSpace(documents))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return Path.Combine(documents, "sparkbooth");
|
||
}
|
||
}
|