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);
}
}
}