Der Ordner bestimmt die Serie, nicht der Dateiname
Aus einer Serie wurden siebenhundert. Dateien wie "S01E001_Aang.mkv" tragen vor der Folgennummer nichts, also wurde der ganze Dateiname zum Serientitel — jede Folge eine eigene Serie. Und umgekehrt ergab "Doctor Who (2005) - S12E04.mkv" im Ordner "Doctor Who" zwei Serien, weil die Jahresangabe nur im Dateinamen stand. Jetzt gilt: was unter Serien/Pingu/ liegt, gehört zu Pingu. Egal wie tief, egal wie die Datei heisst. Der Ordner ist die verlässlichere Angabe, weil ihn ein Mensch angelegt hat; so macht es Plex seit je und aus demselben Grund. Staffel und Folge liest weiterhin der Dateiname. Ohne bekannte Wurzel bleibt es beim Dateinamen — dort könnte über dem Staffelordner irgendetwas stehen. Diesen Weg gehen nur die Tests. Dazu vier Dinge aus der Benutzung: Die Mediatheken standen scheinbar in zufälliger Reihenfolge. SQLite vergleicht Text zeichengenau, und dort stehen alle Grossbuchstaben vor allen kleinen: "Serien" landete über "film". Jetzt ohne Rücksicht auf Gross- und Kleinschreibung — und umsortierbar über zwei Pfeile. Nicht über Ziehen: das ist schöner, aber mit der Tastatur unbedienbar und auf dem Telefon ein Glücksspiel. Die Sprache des Servers steckte im Dialog "Metadaten", zusammen mit dem TMDB-Schlüssel — also dort, wo niemand nach einer Sprache sucht. Wer die Mediathek auf Englisch vorfand, sah nur "Meine Sprache" und hielt das für die einzige Möglichkeit. Jetzt eine eigene Zeile. Das Kontextmenü gab es nur per Rechtsklick, und den findet nur, wer ihn vermutet — auf dem Telefon gibt es ihn gar nicht. Jetzt drei Punkte auf jeder Kachel, beim Überfahren und bei Tastaturfokus, auf Geräten ohne Zeiger dauerhaft. Und "Zuordnung korrigieren" steht endlich darin: wer zwanzig falsch erkannte Titel vor sich hat, soll sie dort geradeziehen können statt über vierzig Seitenwechsel. Folgen ohne eigenes Bild nehmen das der Serie. TMDB liefert Folgenbilder nur für einen Teil der Folgen und bei kleineren Serien für gar keine; eine graue Fläche sagt nichts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
fa18c0d7fa
commit
98565e14b5
@@ -186,7 +186,11 @@ public static partial class FileNames
|
||||
/// ein Film oder etwas, das niemand benennen kann — in beiden Fällen wäre
|
||||
/// eine geratene Staffel 1 schlimmer als gar keine.
|
||||
/// </remarks>
|
||||
public static EpisodeName? ParseEpisode(string dateiname)
|
||||
/// <param name="wurzel">
|
||||
/// Der Wurzelpfad der Mediathek. Ist er angegeben, bestimmt der erste
|
||||
/// Ordner darunter die Serie — und zwar <b>allein</b>.
|
||||
/// </param>
|
||||
public static EpisodeName? ParseEpisode(string dateiname, string? wurzel = null)
|
||||
{
|
||||
var roh = Rohname(dateiname);
|
||||
|
||||
@@ -206,33 +210,67 @@ public static partial class FileNames
|
||||
var (worteDanach, _) = Zerlegen(danach, istDateiname: false);
|
||||
var (folgenTitel, _) = TitelBis(worteDanach, worteDanach.Length);
|
||||
|
||||
// **Steht im Dateinamen keine Serie, sagt der Ordner sie.**
|
||||
// **Der Ordner entscheidet, nicht der Dateiname.**
|
||||
//
|
||||
// Bei „Dark/Staffel 1/S01E01.mkv" steht vor der Folgennummer nichts —
|
||||
// und bis hierher wurde dann der ganze Dateiname zum Serientitel.
|
||||
// Jede so benannte Datei ergab damit eine eigene Serie: aus zwanzig
|
||||
// Folgen wurden zwanzig Serien namens „S01E01", „S01E02", …
|
||||
// Was unter „Serien/Pingu/" liegt, gehört zu Pingu — ohne Rücksicht
|
||||
// darauf, wie die Datei heisst. Vorher entschied der Dateiname, und
|
||||
// das ging reihenweise schief: Dateien namens „S01E001_Aang.mkv"
|
||||
// tragen vor der Folgennummer nichts, also wurde der ganze Dateiname
|
||||
// zum Serientitel. Aus einer Serie mit zwanzig Folgen wurden zwanzig
|
||||
// Serien.
|
||||
//
|
||||
// Plex und Jellyfin lesen den Namen seit je aus dem Ordner. Der
|
||||
// Staffelordner wird dabei übersprungen, sonst hiesse die Serie
|
||||
// „Staffel 1".
|
||||
var ausOrdner = serie.Length > 0 ? null : SerieAusOrdner(dateiname);
|
||||
// Umgekehrt ebenso wichtig: „Doctor Who (2005) - S12E04.mkv" im Ordner
|
||||
// „Doctor Who" ergab zwei Serien, weil die Jahresangabe im Dateinamen
|
||||
// stand und im Ordner nicht. Der Ordner ist die verlässlichere Angabe,
|
||||
// weil ihn ein Mensch angelegt hat.
|
||||
//
|
||||
// So macht es Plex seit je, und aus demselben Grund.
|
||||
// **Mit Wurzel entscheidet der Ordner, ohne Wurzel nur im Notfall.**
|
||||
//
|
||||
// Steht die Wurzel fest, weiss man, welcher Ordner die Serie benennt —
|
||||
// der erste darunter, von einem Menschen angelegt. Fehlt sie, könnte
|
||||
// über dem Staffelordner irgendetwas stehen; dann ist der Dateiname
|
||||
// die bessere Angabe, und der Ordner greift nur, wenn er gar nichts
|
||||
// hergibt.
|
||||
var ausOrdner = wurzel is { Length: > 0 }
|
||||
? SerieAusOrdner(dateiname, wurzel)
|
||||
: serie.Length > 0 ? null : SerieAusOrdner(dateiname, null);
|
||||
|
||||
return new EpisodeName(
|
||||
SeriesTitle: serie.Length > 0 ? serie : ausOrdner ?? Rohname(dateiname),
|
||||
SeriesTitle: ausOrdner ?? (serie.Length > 0 ? serie : Rohname(dateiname)),
|
||||
Season: staffel,
|
||||
Episode: folge,
|
||||
EpisodeTitle: folgenTitel.Length > 0 ? folgenTitel : null);
|
||||
}
|
||||
|
||||
/// <summary>Der Serienname aus dem Pfad — Staffelordner übersprungen.</summary>
|
||||
/// <summary>Der Serienname aus dem Pfad.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Höchstens zwei Ebenen hoch.</b> Weiter oben steht der Wurzelordner
|
||||
/// der Mediathek, und „Serien" ist kein Serientitel.
|
||||
/// <para>
|
||||
/// <b>Mit Wurzel: der erste Ordner darunter.</b> Alles, was in
|
||||
/// <c>Serien/Pingu/</c> liegt, gehört zu Pingu — egal wie tief und egal
|
||||
/// wie die Datei heisst. Das ist die Regel, die ein Mensch beim Anlegen
|
||||
/// der Ordner im Kopf hatte.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Ohne Wurzel: höchstens zwei Ebenen hoch</b>, Staffelordner
|
||||
/// übersprungen. Diesen Weg gehen nur die Tests, die mit relativen Pfaden
|
||||
/// arbeiten.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private static string? SerieAusOrdner(string pfad)
|
||||
private static string? SerieAusOrdner(string pfad, string? wurzel)
|
||||
{
|
||||
if (wurzel is { Length: > 0 })
|
||||
{
|
||||
var unterhalb = UnterWurzel(pfad, wurzel);
|
||||
// Liegt die Datei direkt in der Wurzel, gibt es keinen Ordner, der
|
||||
// sie benennt — dann bleibt nur der Dateiname.
|
||||
if (unterhalb is null || !unterhalb.Contains('/')) return null;
|
||||
|
||||
var erster = unterhalb[..unterhalb.IndexOf('/')];
|
||||
return Saubermachen(erster);
|
||||
}
|
||||
|
||||
var ordner = Path.GetDirectoryName(pfad);
|
||||
|
||||
for (var ebene = 0; ebene < 2 && !string.IsNullOrEmpty(ordner); ebene++)
|
||||
@@ -240,12 +278,7 @@ public static partial class FileNames
|
||||
var name = Path.GetFileName(ordner);
|
||||
if (name.Length == 0) return null;
|
||||
|
||||
if (!IstStaffelordner(name))
|
||||
{
|
||||
var (worte, _) = Zerlegen(name, istDateiname: false);
|
||||
var (titel, _) = TitelBis(worte, worte.Length);
|
||||
return titel.Length > 0 ? titel : null;
|
||||
}
|
||||
if (!IstStaffelordner(name)) return Saubermachen(name);
|
||||
|
||||
ordner = Path.GetDirectoryName(ordner);
|
||||
}
|
||||
@@ -253,6 +286,23 @@ public static partial class FileNames
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>Der Pfad unterhalb der Wurzel, mit „/" getrennt.</summary>
|
||||
private static string? UnterWurzel(string pfad, string wurzel)
|
||||
{
|
||||
var p = pfad.Replace('\\', '/');
|
||||
var w = wurzel.Replace('\\', '/').TrimEnd('/');
|
||||
|
||||
if (!p.StartsWith(w + "/", StringComparison.OrdinalIgnoreCase)) return null;
|
||||
return p[(w.Length + 1)..];
|
||||
}
|
||||
|
||||
private static string? Saubermachen(string ordnername)
|
||||
{
|
||||
var (worte, _) = Zerlegen(ordnername, istDateiname: false);
|
||||
var (titel, _) = TitelBis(worte, worte.Length);
|
||||
return titel.Length > 0 ? titel : null;
|
||||
}
|
||||
|
||||
/// <summary>„Staffel 1", „Season 02", „S3" — Ordner, die keine Serie benennen.</summary>
|
||||
private static bool IstStaffelordner(string name) =>
|
||||
StaffelOrdnerName().IsMatch(name.Trim());
|
||||
|
||||
Reference in New Issue
Block a user