namespace Flaro.Core.Library;
/// Welche Dateien Flaro neben eine Mediendatei legen darf — und sonst keine.
///
///
///
/// Eine Erlaubnisliste, und sie ist der ganze Schutz. Damit Plakate und
/// Metadaten im Filmordner landen können, muss die Einhängung schreibbar
/// sein — und damit kann der Server im Grundsatz alles dort anstellen. Dass
/// er es nicht tut, hängt an dieser Liste und an der Prüfung darunter.
///
///
/// Mediendateien werden nie angefasst. Nicht geändert, nicht
/// umbenannt, nicht gelöscht. Es gibt im ganzen Projekt keine Stelle, die das
/// könnte, und diese Liste ist der Grund: was nicht daraufsteht, wird
/// abgelehnt — auch wenn jemand es ausdrücklich verlangt.
///
///
/// Transcode-Zwischenstände gehören ausdrücklich nicht hierher. Die
/// liegen unter FLARO_TRANSCODE, auf einem eigenen Volume. Ein
/// Medienordner ist kein Transcode-Cache.
///
///
public static class SidecarNames
{
public const string MoviePoster = "poster.jpg";
public const string MovieBackdrop = "fanart.jpg";
public const string MovieLogo = "logo.png";
public const string MovieNfo = "movie.nfo";
public const string SeriesPoster = "poster.jpg";
public const string SeriesBackdrop = "fanart.jpg";
public const string SeriesNfo = "tvshow.nfo";
/// Was zu einer Folge gehört, hängt am Namen ihrer Datei.
///
///
/// Aus Dark - S01E01.mkv werden Dark - S01E01.nfo und
/// Dark - S01E01-thumb.jpg. So erwarten es Kodi, Jellyfin und Plex
/// — und so findet auch ein Mensch sie wieder.
///
public static string EpisodeNfo(string mediendatei) =>
Path.GetFileNameWithoutExtension(mediendatei) + ".nfo";
public static string EpisodeThumb(string mediendatei) =>
Path.GetFileNameWithoutExtension(mediendatei) + "-thumb.jpg";
/// Beiwerk für einen Film, der sich den Ordner mit anderen teilt.
///
///
/// Sonst überschreiben sich zwei Filme gegenseitig das Plakat. Wer
/// je Film einen Ordner anlegt, bekommt die schlichten Namen; wer zehn
/// Filme nebeneinanderlegt, bekommt sie mit dem Dateinamen davor. Kodi und
/// Jellyfin lesen beide Formen.
///
public static string Prefixed(string mediendatei, string beiwerk) =>
Path.GetFileNameWithoutExtension(mediendatei) + "-" + beiwerk;
private static readonly HashSet Feste = new(StringComparer.OrdinalIgnoreCase)
{ MoviePoster, MovieBackdrop, MovieLogo, MovieNfo, SeriesNfo };
private static readonly string[] Endungen =
[".nfo", "-thumb.jpg", "-poster.jpg", "-fanart.jpg", "-logo.png"];
/// Ob dieser Dateiname geschrieben werden darf.
///
///
/// Geprüft wird der Name, nicht die Absicht. Ein Pfadbestandteil,
/// ein Verzeichniswechsel oder ein führender Punkt kommen hier nicht
/// durch — und „film.mkv" erst recht nicht, auch wenn es auf .nfo
/// endete.
///
public static bool MayWrite(string dateiname)
{
if (string.IsNullOrWhiteSpace(dateiname)) return false;
// Kein Pfad, nur ein Name. Alles andere könnte aus dem Ordner führen.
if (dateiname.Contains('/') || dateiname.Contains('\\')) return false;
if (dateiname != Path.GetFileName(dateiname)) return false;
if (dateiname.StartsWith('.')) return false;
if (dateiname.Contains("..", StringComparison.Ordinal)) return false;
if (Feste.Contains(dateiname)) return true;
// Je Folge: beliebiger Rumpf, aber nur mit diesen Endungen.
return Endungen.Any(e => dateiname.EndsWith(e, StringComparison.OrdinalIgnoreCase)
&& dateiname.Length > e.Length);
}
}