Upload via GUI (180 Dateien)
This commit is contained in:
+140
-111
@@ -1,111 +1,140 @@
|
||||
using System.IO;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace DeskCore;
|
||||
|
||||
/// <summary>
|
||||
/// Beobachtet Desktop und Kategorie-Ordner. Meldet Änderungen gebündelt (statt bei jedem einzelnen Ereignis)
|
||||
/// und sammelt neu auf dem Desktop aufgetauchte Elemente für die automatische Sortierung.
|
||||
/// </summary>
|
||||
public sealed class Watcher : IDisposable
|
||||
{
|
||||
private readonly List<FileSystemWatcher> _watchers = [];
|
||||
private readonly DispatcherTimer _changedTimer;
|
||||
private readonly DispatcherTimer _newTimer;
|
||||
private readonly HashSet<string> _newOnDesktop = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _lock = new();
|
||||
|
||||
/// <summary>Irgendetwas hat sich geändert (UI-Thread).</summary>
|
||||
public event Action? Changed;
|
||||
/// <summary>Neue Elemente auf dem Desktop (UI-Thread, nach kurzer Wartezeit).</summary>
|
||||
public event Action<List<string>>? NewOnDesktop;
|
||||
|
||||
public Watcher(Dispatcher dispatcher)
|
||||
{
|
||||
_changedTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(350), DispatcherPriority.Background, (_, _) =>
|
||||
{
|
||||
_changedTimer!.Stop();
|
||||
Changed?.Invoke();
|
||||
}, dispatcher);
|
||||
_changedTimer.Stop();
|
||||
|
||||
// Etwas länger warten: Installer legen Verknüpfungen oft in mehreren Schritten an
|
||||
_newTimer = new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Background, (_, _) =>
|
||||
{
|
||||
_newTimer!.Stop();
|
||||
List<string> items;
|
||||
lock (_lock) { items = _newOnDesktop.ToList(); _newOnDesktop.Clear(); }
|
||||
items = items.Where(p => (File.Exists(p) || Directory.Exists(p)) && !Storage.CreatedByApp(p)).ToList();
|
||||
if (items.Count > 0) NewOnDesktop?.Invoke(items);
|
||||
}, dispatcher);
|
||||
_newTimer.Stop();
|
||||
|
||||
Directory.CreateDirectory(Storage.Root);
|
||||
Add(Storage.UserDesktop, false, true);
|
||||
Add(Storage.PublicDesktop, false, true);
|
||||
Add(Storage.Root, true, false);
|
||||
}
|
||||
|
||||
private void Add(string dir, bool subdirs, bool isDesktop)
|
||||
{
|
||||
if (!Directory.Exists(dir)) return;
|
||||
try
|
||||
{
|
||||
var w = new FileSystemWatcher(dir)
|
||||
{
|
||||
IncludeSubdirectories = subdirs,
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite,
|
||||
InternalBufferSize = 64 * 1024,
|
||||
};
|
||||
FileSystemEventHandler onChange = (_, e) =>
|
||||
{
|
||||
Poke();
|
||||
if (isDesktop && e.ChangeType == WatcherChangeTypes.Created) NoteNew(e.FullPath);
|
||||
};
|
||||
w.Created += onChange;
|
||||
w.Deleted += onChange;
|
||||
w.Changed += onChange;
|
||||
w.Renamed += (_, e) => { Poke(); if (isDesktop) NoteNew(e.FullPath); };
|
||||
w.Error += (_, _) => Poke();
|
||||
w.EnableRaisingEvents = true;
|
||||
_watchers.Add(w);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void NoteNew(string path)
|
||||
{
|
||||
var name = Path.GetFileName(path);
|
||||
if (name.StartsWith('~') || name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase) ||
|
||||
name.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase)) return;
|
||||
lock (_lock) _newOnDesktop.Add(path);
|
||||
_newTimer.Dispatcher.BeginInvoke(() => { _newTimer.Stop(); _newTimer.Start(); });
|
||||
}
|
||||
|
||||
private void Poke() => _changedTimer.Dispatcher.BeginInvoke(() => { _changedTimer.Stop(); _changedTimer.Start(); });
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var w in _watchers) w.Dispose();
|
||||
_changedTimer.Stop();
|
||||
_newTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AutoSorter
|
||||
{
|
||||
/// <summary>Erste passende Regel → Kategoriename (nur existierende Kategorien).</summary>
|
||||
public static string? Match(string path)
|
||||
{
|
||||
var name = Storage.DisplayName(path);
|
||||
var cats = Storage.Categories();
|
||||
foreach (var r in AppSettings.Current.Rules)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(r.Contains)) continue;
|
||||
if (!name.Contains(r.Contains.Trim(), StringComparison.CurrentCultureIgnoreCase)) continue;
|
||||
var cat = cats.FirstOrDefault(c => string.Equals(c, r.Category, StringComparison.OrdinalIgnoreCase));
|
||||
if (cat is not null) return cat;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
using System.IO;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace DeskCore;
|
||||
|
||||
/// <summary>
|
||||
/// Beobachtet Desktop und Kategorie-Ordner. Meldet Änderungen gebündelt (statt bei jedem einzelnen Ereignis)
|
||||
/// und sammelt neu auf dem Desktop aufgetauchte Elemente für die automatische Sortierung.
|
||||
/// </summary>
|
||||
public sealed class Watcher : IDisposable
|
||||
{
|
||||
private readonly List<FileSystemWatcher> _watchers = [];
|
||||
private readonly DispatcherTimer _changedTimer;
|
||||
private readonly DispatcherTimer _newTimer;
|
||||
private readonly HashSet<string> _newOnDesktop = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly object _lock = new();
|
||||
|
||||
/// <summary>Irgendetwas hat sich geändert (UI-Thread).</summary>
|
||||
public event Action? Changed;
|
||||
/// <summary>Neue Elemente auf dem Desktop (UI-Thread, nach kurzer Wartezeit).</summary>
|
||||
public event Action<List<string>>? NewOnDesktop;
|
||||
|
||||
public Watcher(Dispatcher dispatcher)
|
||||
{
|
||||
_changedTimer = new DispatcherTimer(TimeSpan.FromMilliseconds(350), DispatcherPriority.Background, (_, _) =>
|
||||
{
|
||||
_changedTimer!.Stop();
|
||||
Changed?.Invoke();
|
||||
}, dispatcher);
|
||||
_changedTimer.Stop();
|
||||
|
||||
// Etwas länger warten: Installer legen Verknüpfungen oft in mehreren Schritten an
|
||||
_newTimer = new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Background, (_, _) =>
|
||||
{
|
||||
_newTimer!.Stop();
|
||||
List<string> items;
|
||||
lock (_lock) { items = _newOnDesktop.ToList(); _newOnDesktop.Clear(); }
|
||||
items = items.Where(p => (File.Exists(p) || Directory.Exists(p)) && !Storage.CreatedByApp(p)).ToList();
|
||||
if (items.Count > 0) NewOnDesktop?.Invoke(items);
|
||||
}, dispatcher);
|
||||
_newTimer.Stop();
|
||||
|
||||
Directory.CreateDirectory(Storage.Root);
|
||||
Add(Storage.UserDesktop, false, true);
|
||||
Add(Storage.PublicDesktop, false, true);
|
||||
Add(Storage.Root, true, false);
|
||||
}
|
||||
|
||||
private void Add(string dir, bool subdirs, bool isDesktop)
|
||||
{
|
||||
if (!Directory.Exists(dir)) return;
|
||||
try
|
||||
{
|
||||
var w = new FileSystemWatcher(dir)
|
||||
{
|
||||
IncludeSubdirectories = subdirs,
|
||||
NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite,
|
||||
InternalBufferSize = 64 * 1024,
|
||||
};
|
||||
FileSystemEventHandler onChange = (_, e) =>
|
||||
{
|
||||
Poke();
|
||||
if (isDesktop && e.ChangeType == WatcherChangeTypes.Created) NoteNew(e.FullPath);
|
||||
};
|
||||
w.Created += onChange;
|
||||
w.Deleted += onChange;
|
||||
w.Changed += onChange;
|
||||
w.Renamed += (_, e) => { Poke(); if (isDesktop) NoteNew(e.FullPath); };
|
||||
w.Error += (_, _) => Poke();
|
||||
w.EnableRaisingEvents = true;
|
||||
_watchers.Add(w);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void NoteNew(string path)
|
||||
{
|
||||
var name = Path.GetFileName(path);
|
||||
if (name.StartsWith('~') || name.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase) ||
|
||||
name.Equals("desktop.ini", StringComparison.OrdinalIgnoreCase)) return;
|
||||
lock (_lock) _newOnDesktop.Add(path);
|
||||
_newTimer.Dispatcher.BeginInvoke(() => { _newTimer.Stop(); _newTimer.Start(); });
|
||||
}
|
||||
|
||||
private void Poke() => _changedTimer.Dispatcher.BeginInvoke(() => { _changedTimer.Stop(); _changedTimer.Start(); });
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var w in _watchers) w.Dispose();
|
||||
_changedTimer.Stop();
|
||||
_newTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AutoSorter
|
||||
{
|
||||
/// <summary>Dateityp-Gruppen für Regeln vom Typ "Dateityp ist".</summary>
|
||||
private static readonly Dictionary<string, string[]> Groups = new(StringComparer.CurrentCultureIgnoreCase)
|
||||
{
|
||||
["Bilder"] = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "heic", "tif", "tiff", "svg", "ico", "raw", "psd"],
|
||||
["Videos"] = ["mp4", "mkv", "avi", "mov", "wmv", "webm", "m4v", "flv", "mpg", "mpeg"],
|
||||
["Musik"] = ["mp3", "wav", "flac", "aac", "ogg", "m4a", "wma", "opus"],
|
||||
["Dokumente"] = ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "odt", "ods", "odp", "txt", "rtf", "md", "csv"],
|
||||
["Archive"] = ["zip", "rar", "7z", "tar", "gz", "bz2", "xz", "iso"],
|
||||
["Programme"] = ["exe", "msi", "lnk", "url", "appref-ms", "bat", "cmd"],
|
||||
["Verknüpfungen"] = ["lnk", "url", "appref-ms"],
|
||||
};
|
||||
|
||||
/// <summary>Passt eine Regel auf das Element?</summary>
|
||||
public static bool Matches(SortRule r, string path)
|
||||
{
|
||||
var value = r.Contains.Trim();
|
||||
if (value.Length == 0) return false;
|
||||
if (r.Kind != "Typ") return Storage.DisplayName(path).Contains(value, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
bool isFolder = Directory.Exists(path);
|
||||
var ext = Path.GetExtension(path).TrimStart('.').ToLowerInvariant();
|
||||
foreach (var token in value.Split([',', ';', ' '], StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var t = token.Trim().TrimStart('*', '.').ToLowerInvariant();
|
||||
if (t is "ordner") { if (isFolder) return true; continue; }
|
||||
if (Groups.TryGetValue(t, out var exts)) { if (!isFolder && exts.Contains(ext)) return true; continue; }
|
||||
if (!isFolder && t == ext) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Erste passende Regel → Kategoriename (nur existierende Kategorien).</summary>
|
||||
public static string? Match(string path)
|
||||
{
|
||||
var cats = Storage.Categories();
|
||||
foreach (var r in AppSettings.Current.Rules)
|
||||
{
|
||||
if (!Matches(r, path)) continue;
|
||||
var cat = cats.FirstOrDefault(c => string.Equals(c, r.Category, StringComparison.OrdinalIgnoreCase));
|
||||
if (cat is not null) return cat;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user