Hanni und Nanni ist nicht Hanni und Nanni 3

Deine drei Fälle haben eines gemeinsam, und es ist nicht das, was ich zuerst
vermutet hatte: keiner der Dateinamen nennt ein Jahr. Der Riegel von gestern
greift deshalb nicht.

Gemessen, woran es wirklich liegt. Bei TMDB heisst der Film „Hanni & Nanni",
auf der Platte „Hanni und Nanni". Das Kaufmanns-Und fällt im Vergleichs-
schlüssel als Sonderzeichen heraus, das Wort „und" bleibt stehen — die
Namensgleichheit griff also nie. Gewählt wurde stattdessen der erstbeste
Treffer, und das war „Hanni & Nanni 3". Der richtige stand auf Platz zwei,
und alle vier Treffer hatten dieselbe Beliebtheit: die Reihenfolge war
Zufall.

Bindewörter zwischen zwei Namen zählen jetzt nicht mehr mit — gestrichen,
nicht übersetzt, sonst bliebe „Batman and Robin" aussen vor. Am Wortanfang
bleiben sie stehen: „Und dann kam Polly" ist kein „dann kam Polly". Und die
Suche vergleicht über den Schlüssel statt Zeichen für Zeichen.

Das verhindert aber nur Neues. Was schon zusammengelegt ist, bleibt es —
deshalb gibt es jetzt „Eigener Film": eine Datei löst sich aus ihrem Titel
und wird ein eigener Eintrag, mit dem Titel aus dem Dateinamen und ohne
Kennung. Eine zu raten hiesse, denselben Fehler zu wiederholen; so steht sie
unter „Ohne Zuordnung" und ist in zwei Klicks richtig.

Der Super-Mario-Fall liegt anders: beide Titel treffen heute richtig. Der
steckt noch aus einem früheren Lauf fest und braucht das Trennen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-06 13:40:21 +02:00
co-authored by Claude Opus 5
parent c3124ba27c
commit 58e06d0539
10 changed files with 328 additions and 21 deletions
@@ -512,6 +512,76 @@ public class MetadataTests
});
}
/// <summary>Eine Datei lässt sich aus ihrem Titel herauslösen.</summary>
///
/// <remarks>
/// <b>Weil die Riegel nur Neues verhindern.</b> Was schon zusammengelegt
/// ist, bleibt es — „Hanni und Nanni" und „Hanni und Nanni 3" hängen dann
/// an einem Eintrag, und einer der beiden Filme ist nicht mehr zu sehen.
/// Der neue Eintrag bekommt den Titel aus dem Dateinamen und keine
/// Kennung; eine zu raten hiesse, denselben Fehler zu wiederholen.
/// </remarks>
[Test]
public async Task Eine_Datei_laesst_sich_aus_ihrem_Titel_herausloesen()
{
Datei("Hanni und Nanni/Hanni und Nanni.mp4");
Datei("Hanni und Nanni/Hanni und Nanni 3.mp4");
// Die Quelle liefert für beides denselben Film — der Fall aus dem Betrieb.
var anbieter = new VorgegebenerAnbieter
{
Film = new MovieMetadata(225313, "Hanni & Nanni 3", 2013, null, null, null),
};
using var server = MitAnbieter(anbieter);
var (client, bibliothek) = await Einrichten(server, "Movies");
await Scannen(client, bibliothek);
var mehrfach = await client.GetFromJsonAsync<Mehrfachbericht>(
$"/api/v1/libraries/{bibliothek}/versions");
Assert.That(mehrfach!.Gesamt, Is.EqualTo(1), "zwei Dateien an einem Titel");
var dateien = mehrfach.Titel[0].Dateien;
Assert.That(dateien, Has.Count.EqualTo(2));
var raus = dateien.First(f => f.Name.Contains(" 3"));
var antwort = await client.PostAsync($"/api/v1/files/{raus.Id}/split", null);
antwort.EnsureSuccessStatusCode();
var neu = (await antwort.Content.ReadFromJsonAsync<MatchState>())!;
var filme = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
Assert.Multiple(() =>
{
Assert.That(filme!.Items, Has.Count.EqualTo(2), "jetzt zwei Filme");
Assert.That(neu.Title, Is.EqualTo("Hanni und Nanni 3"), "der Titel kommt aus dem Dateinamen");
Assert.That(neu.TmdbId, Is.Null, "und ohne Kennung — geraten wird nicht");
Assert.That(filme.Items.Select(m => m.Files.Count), Is.All.EqualTo(1));
});
}
/// <summary>Eine einzelne Datei lässt sich nicht herauslösen.</summary>
[Test]
public async Task Eine_einzelne_Datei_laesst_sich_nicht_herausloesen()
{
Datei("Matrix (1999)/Matrix (1999).mkv");
var anbieter = new VorgegebenerAnbieter
{
Film = new MovieMetadata(603, "Matrix", 1999, null, null, null),
};
using var server = MitAnbieter(anbieter);
var (client, bibliothek) = await Einrichten(server, "Movies");
await Scannen(client, bibliothek);
var filme = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
var datei = filme!.Items[0].Files.Single();
var antwort = await client.PostAsync($"/api/v1/files/{datei.Id}/split", null);
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.Conflict));
}
/// <summary>Eine falsche Zuordnung darf ihre eigene Korrektur nicht verhindern.</summary>
///
/// <remarks>
@@ -84,4 +84,58 @@ public class FassungTests
Assert.That(FileNames.ParseMovie("The Matrix (1999) - Mobil.mp4").Title, Is.EqualTo("The Matrix"));
Assert.That(FileNames.ParseMovie("The Matrix (1999) - 2160p.mkv").Title, Is.EqualTo("The Matrix"));
}
/* ---------- Bindewörter ---------- */
/// <summary>„Hanni und Nanni" und „Hanni &amp; Nanni" sind derselbe Titel.</summary>
///
/// <remarks>
/// <b>Der Fall, an dem ein ganzer Film verschwand.</b> Auf der Platte
/// heisst er „Hanni und Nanni", bei TMDB „Hanni &amp; Nanni" — das
/// Kaufmanns-Und fiel als Sonderzeichen heraus, das Wort „und" blieb
/// stehen. Die Namensgleichheit griff deshalb nie, und gewählt wurde der
/// erstbeste Treffer: „Hanni &amp; Nanni 3". Beide Dateien landeten auf
/// derselben Kennung, und Teil 1 war nicht mehr zu sehen.
/// </remarks>
[TestCase("Hanni und Nanni", "Hanni & Nanni")]
[TestCase("Batman and Robin", "Batman & Robin")]
[TestCase("Tom und Jerry", "Tom & Jerry")]
public void Bindewoerter_machen_keinen_Unterschied(string eines, string anderes)
{
Assert.That(Titelschluessel.Von(eines), Is.EqualTo(Titelschluessel.Von(anderes)));
}
/// <summary>Eine Folgennummer bleibt ein Unterschied.</summary>
[Test]
public void Eine_Nummer_bleibt_ein_Unterschied()
{
Assert.That(Titelschluessel.Von("Hanni und Nanni"),
Is.Not.EqualTo(Titelschluessel.Von("Hanni & Nanni 3")));
}
/// <summary>Am Anfang gehört das Wort zum Titel.</summary>
///
/// <remarks>
/// „Und dann kam Polly" fängt mit einem Bindewort an — es wegzulassen
/// machte daraus „dann kam Polly", und das ist ein anderer Film.
/// </remarks>
[TestCase("Und dann kam Polly")]
[TestCase("And Then There Were None")]
public void Am_Anfang_bleibt_das_Wort_stehen(string titel)
{
Assert.That(Titelschluessel.Von(titel), Does.StartWith(
titel.Split(' ')[0].ToLowerInvariant()));
}
/// <summary>Zwei Wörter bleiben zwei Wörter.</summary>
///
/// <remarks>
/// Bei „Ich und" bliebe sonst nur „Ich" übrig — und das passt dann auf
/// jeden Film, der so anfängt.
/// </remarks>
[Test]
public void Aus_zwei_Woertern_wird_nichts_gestrichen()
{
Assert.That(Titelschluessel.Von("Krieg und"), Is.EqualTo("krieg und"));
}
}