namespace Halo.Core.Library; /// Eine gefundene Datei, so wie das Dateisystem sie beschreibt. public sealed record ScannedFile(string Path, long SizeBytes, DateTimeOffset ModifiedAt); /// Was beim Durchlaufen schiefging, ohne den Lauf abzubrechen. public sealed record WalkProblem(string Path, string Reason); public sealed record WalkResult(IReadOnlyList Files, IReadOnlyList Problems); /// Läuft ein Bibliotheksverzeichnis ab und sammelt Videodateien. /// /// /// /// Von Hand rekursiv statt mit AllDirectories. Die bequeme /// Fassung läuft auch in @eaDir hinein — Synologys Vorschauablage, die /// eine Kopie jeder Datei enthält. Wer sie mitnimmt, hat seine /// Mediathek doppelt, zur Hälfte mit unbrauchbaren Dateien. Äste beschneiden /// kann nur, wer selbst absteigt. /// /// /// Ein gesperrtes Unterverzeichnis bricht den Lauf nicht ab, sondern /// wird vermerkt. Sonst entschiede ein einzelner falsch gesetzter Ordner /// darüber, ob überhaupt etwas gefunden wird — und die Mediathek bliebe leer, /// ohne dass jemand sagen könnte, warum. /// /// public static class LibraryWalker { /// Tiefer als das ist kein Bestand, sondern eine Schleife. /// /// /// Verweise im Dateisystem können im Kreis zeigen. Ohne Grenze liefe der /// Scan, bis der Arbeitsspeicher voll ist. /// private const int MaximaleTiefe = 12; public static WalkResult Walk(string wurzel) { var gefunden = new List(); var probleme = new List(); if (!Directory.Exists(wurzel)) { probleme.Add(new WalkProblem(wurzel, "Das Verzeichnis gibt es nicht.")); return new WalkResult(gefunden, probleme); } Absteigen(wurzel, 0, gefunden, probleme); return new WalkResult(gefunden, probleme); } private static void Absteigen(string ordner, int tiefe, List gefunden, List probleme) { if (tiefe > MaximaleTiefe) { probleme.Add(new WalkProblem(ordner, $"Tiefer als {MaximaleTiefe} Ebenen — hier wird nicht weitergesucht.")); return; } string[] dateien, unterordner; try { dateien = Directory.GetFiles(ordner); unterordner = Directory.GetDirectories(ordner); } catch (Exception fehler) when (fehler is UnauthorizedAccessException or IOException) { probleme.Add(new WalkProblem(ordner, fehler.Message)); return; } foreach (var datei in dateien) { if (!MediaExtensions.IsVideo(datei)) continue; try { var angaben = new FileInfo(datei); gefunden.Add(new ScannedFile(datei, angaben.Length, angaben.LastWriteTimeUtc)); } catch (Exception fehler) when (fehler is UnauthorizedAccessException or IOException) { probleme.Add(new WalkProblem(datei, fehler.Message)); } } foreach (var unter in unterordner) { if (MediaExtensions.IsSkippedDirectory(Path.GetFileName(unter))) continue; Absteigen(unter, tiefe + 1, gefunden, probleme); } } }