Ein Film kommt je Mediathek genau einmal vor

Der Scan verglich den Titel aus dem Dateinamen zeichengenau. Ein Punkt
Unterschied — Dr. Dolittle 2 gegen Dr Dolittle 2 —, oder eine Datei mit
Jahresangabe neben einer ohne, und es entstanden zwei Einträge. Die
Metadaten ordneten beide demselben TMDB-Eintrag zu, beide bekamen
denselben Titel, dasselbe Jahr, dasselbe Plakat, und nichts führte sie je
wieder zusammen.

Drei Ebenen, weil eine nicht trägt.

Titelschluessel vergleicht ohne Satzzeichen, ohne Akzente und ohne
Grossschreibung; ein fehlendes Jahr passt auf jedes, zwei bekannte
verschiedene trennen. Bewusst nicht aggressiver: Spider-Man und
Spiderman bleiben getrennt, und die beiden Dune von 1984 und 2021
sowieso. Ein zusammengelegter Film lässt sich nicht mehr trennen, ohne
zu wissen, welche Datei wohin gehörte.

Ein gefilterter Eindeutigkeitsindex auf (LibraryId, TmdbId) stellt die
Regel in die Datenbank statt in Code, der sie einhalten soll. Gefiltert,
weil ohne Metadaten alle Einträge null tragen.

Zusammenlegung räumt auf, was kein Vergleich erkennen könnte: Ich
einfach unverbesserlich und Despicable Me sind derselbe Film, und das
verrät allein die TMDB-Kennung. Läuft nach jedem Scan, und von Hand über
einen Knopf in der Verwaltung. Der Sehfortschritt wird gefaltet, nicht
umgehängt — die weitere Stelle gewinnt, gesehen gewinnt, weggelegt
gewinnt nicht.

Die Migration räumt zuerst auf und legt dann erst den Index an.
Andersherum scheiterte sie auf jeder Datenbank mit Dubletten, und zwar
beim Start im Container, wo die Meldung niemand sieht. Gemessen an einer
Datenbank mit zwei Dubletten und zwei Sehständen: 3 Filme auf 2, beide
Dateien erhalten, Position 1800 statt 300 behalten.

Dazu Fassungen. MediaFile.VersionLabel liest Mobil, 4K oder Director's
Cut aus dem Namen, sonst aus der gemessenen Bildhöhe. Die Liste ist
geschlossen, damit eine Fassung nicht YIFY heisst. Und der Fassungsname
beendet jetzt den Titel wie jedes andere Beiwerk — vorher wurde aus
"The Matrix (1999) - Mobil.mp4" der Film "The Matrix - Mobil", also ein
zweiter Eintrag neben dem Original. Genau die Dublette, um die es ging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-04 11:39:10 +02:00
co-authored by Claude Opus 5
parent d01c5d43f4
commit 63c7ebb6d5
16 changed files with 1743 additions and 9 deletions
+31
View File
@@ -113,6 +113,7 @@ public sealed class FlaroContext(DbContextOptions<FlaroContext> options) : DbCon
// ein zweimal angestossener Scan jede Datei zweimal an, und die
// Mediathek wächst bei jedem Lauf.
e.HasIndex(x => new { x.LibraryId, x.Path }).IsUnique();
e.Property(x => x.VersionLabel).HasMaxLength(64);
e.HasIndex(x => x.MovieId);
e.HasIndex(x => x.EpisodeId);
e.Property(x => x.Path).HasMaxLength(1024);
@@ -125,6 +126,21 @@ public sealed class FlaroContext(DbContextOptions<FlaroContext> options) : DbCon
{
e.HasKey(x => x.Id);
e.HasIndex(x => new { x.LibraryId, x.SortTitleValue });
// **Ein Film kommt je Mediathek genau einmal vor.**
//
// Der Scan ordnet über den Dateinamen zu, lange bevor jemand
// weiss, welcher Film das ist — weichen zwei Dateien darin ab,
// entstehen zwei Einträge, die anschliessend beide demselben
// TMDB-Eintrag zugeordnet werden. Hier steht die Regel, die das
// unmöglich macht, statt nur Code, der sie einhalten soll.
//
// Gefiltert, weil ohne Metadaten alle Einträge `null` tragen und
// ein ungefilterter Index dann nur einen einzigen zuliesse.
e.HasIndex(x => new { x.LibraryId, x.TmdbId })
.IsUnique()
.HasFilter("\"TmdbId\" IS NOT NULL");
e.Property(x => x.Title).HasMaxLength(512);
e.Property(x => x.SortTitleValue).HasMaxLength(512);
e.Property(x => x.ImdbId).HasMaxLength(32);
@@ -138,6 +154,21 @@ public sealed class FlaroContext(DbContextOptions<FlaroContext> options) : DbCon
{
e.HasKey(x => x.Id);
e.HasIndex(x => new { x.LibraryId, x.SortTitleValue });
// **Eine Serie kommt je Mediathek genau einmal vor.**
//
// Der Scan ordnet über den Dateinamen zu, lange bevor jemand
// weiss, welcher Film das ist — weichen zwei Dateien darin ab,
// entstehen zwei Einträge, die anschliessend beide demselben
// TMDB-Eintrag zugeordnet werden. Hier steht die Regel, die das
// unmöglich macht, statt nur Code, der sie einhalten soll.
//
// Gefiltert, weil ohne Metadaten alle Einträge `null` tragen und
// ein ungefilterter Index dann nur einen einzigen zuliesse.
e.HasIndex(x => new { x.LibraryId, x.TmdbId })
.IsUnique()
.HasFilter("\"TmdbId\" IS NOT NULL");
e.Property(x => x.Title).HasMaxLength(512);
e.Property(x => x.SortTitleValue).HasMaxLength(512);
e.Property(x => x.ImdbId).HasMaxLength(32);