Dieselbe Kennung zweimal zu vergeben war kein Fehler, sah aber so aus
Im Zuordnungsdialog stand „An error occurred while processing your request." — englisch, ohne Grund, ohne nächsten Schritt. Dahinter steckte der Eindeutigkeitsindex: der gewählte Film war in derselben Mediathek schon vergeben, und die Kennung wurde trotzdem blind gesetzt. Es ist aber gar kein Fehler. Zwei Dateien, die denselben Film meinen, gehören zusammengelegt — genau das tut der Scan am Ende jedes Laufs, und der Sehfortschritt wird dabei gefaltet. Die Korrektur von Hand tut es jetzt auch, für Filme wie für Serien. Dazu die zweite Hälfte: auch das Letzte, was durchrutscht, spricht Deutsch. Der Fehlerbehandler am Ende der Kette gab bisher die Vorgabe von ASP.NET aus. Jetzt bekommt jede Ausnahme einen deutschen Satz, und die Einzelheiten gehen ins Tagesprotokoll — dorthin, wo man sie sucht. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0c07d4efc7
commit
5a5cf3d006
@@ -149,7 +149,7 @@ public static class MatchEndpoints
|
||||
|
||||
app.MapPut("/api/v1/movies/{id:guid}/match", async (
|
||||
Guid id, ApplyMatchRequest anfrage, FlaroContext db,
|
||||
IMetadataProvider anbieter, MatchApplier anwender) =>
|
||||
IMetadataProvider anbieter, MatchApplier anwender, Zusammenlegung zusammen) =>
|
||||
{
|
||||
var film = await db.Movies.FirstOrDefaultAsync(m => m.Id == id);
|
||||
if (film is null) return Results.NotFound();
|
||||
@@ -159,6 +159,33 @@ public static class MatchEndpoints
|
||||
return Results.Problem($"TMDB kennt den Film {anfrage.TmdbId} nicht.",
|
||||
statusCode: StatusCodes.Status404NotFound);
|
||||
|
||||
// **Hat die Kennung schon jemand, ist es derselbe Film.**
|
||||
//
|
||||
// Ein Film kommt je Mediathek genau einmal vor — das erzwingt ein
|
||||
// Eindeutigkeitsindex. Die Kennung blind zu setzen liess ihn
|
||||
// zuschlagen, und der Benutzer bekam „An error occurred while
|
||||
// processing your request." zu sehen: eine englische
|
||||
// Allgemeinmeldung für einen Fall, der gar kein Fehler ist.
|
||||
//
|
||||
// Zwei Dateien, die denselben Film meinen, gehören zusammengelegt
|
||||
// — genau das tut der Scan am Ende jedes Laufs auch. Der
|
||||
// Sehfortschritt wird dabei gefaltet, nichts geht verloren.
|
||||
var schonDa = await db.Movies
|
||||
.Where(m => m.LibraryId == film.LibraryId && m.TmdbId == anfrage.TmdbId && m.Id != id)
|
||||
.Select(m => m.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (schonDa != Guid.Empty)
|
||||
{
|
||||
await anwender.ApplyAsync(
|
||||
(await db.Movies.FirstAsync(m => m.Id == schonDa)), gefunden);
|
||||
await zusammen.FilmVerschmelzenAsync(sieger: schonDa, verlierer: id);
|
||||
|
||||
var sieger = await db.Movies.AsNoTracking().FirstAsync(m => m.Id == schonDa);
|
||||
return Results.Ok(new MatchState(
|
||||
sieger.TmdbId, sieger.ImdbId, sieger.MatchLocked, sieger.Title, sieger.Year));
|
||||
}
|
||||
|
||||
await anwender.ApplyAsync(film, gefunden);
|
||||
return Results.Ok(new MatchState(film.TmdbId, film.ImdbId, film.MatchLocked, film.Title, film.Year));
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
@@ -180,7 +207,7 @@ public static class MatchEndpoints
|
||||
|
||||
app.MapPut("/api/v1/series/{id:guid}/match", async (
|
||||
Guid id, ApplyMatchRequest anfrage, FlaroContext db,
|
||||
IMetadataProvider anbieter, MatchApplier anwender) =>
|
||||
IMetadataProvider anbieter, MatchApplier anwender, Zusammenlegung zusammen) =>
|
||||
{
|
||||
var serie = await db.Series.FirstOrDefaultAsync(s => s.Id == id);
|
||||
if (serie is null) return Results.NotFound();
|
||||
@@ -190,6 +217,23 @@ public static class MatchEndpoints
|
||||
return Results.Problem($"TMDB kennt die Serie {anfrage.TmdbId} nicht.",
|
||||
statusCode: StatusCodes.Status404NotFound);
|
||||
|
||||
// Siehe bei den Filmen: dieselbe Kennung heisst dieselbe Serie,
|
||||
// und zwei Ordner derselben Serie gehören zusammengelegt.
|
||||
var schonDa = await db.Series
|
||||
.Where(x => x.LibraryId == serie.LibraryId && x.TmdbId == anfrage.TmdbId && x.Id != id)
|
||||
.Select(x => x.Id)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (schonDa != Guid.Empty)
|
||||
{
|
||||
await anwender.ApplyAsync(await db.Series.FirstAsync(x => x.Id == schonDa), gefunden);
|
||||
await zusammen.SerieVerschmelzenAsync(sieger: schonDa, verlierer: id);
|
||||
|
||||
var sieger = await db.Series.AsNoTracking().FirstAsync(x => x.Id == schonDa);
|
||||
return Results.Ok(new MatchState(
|
||||
sieger.TmdbId, sieger.ImdbId, sieger.MatchLocked, sieger.Title, sieger.Year));
|
||||
}
|
||||
|
||||
await anwender.ApplyAsync(serie, gefunden);
|
||||
return Results.Ok(new MatchState(serie.TmdbId, serie.ImdbId, serie.MatchLocked, serie.Title, serie.Year));
|
||||
}).RequireAuthorization(FlaroPolicies.Administrator);
|
||||
|
||||
@@ -156,7 +156,33 @@ using (var bereich = app.Services.CreateScope())
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
|
||||
app.UseExceptionHandler();
|
||||
// **Auch das Letzte, was durchrutscht, spricht Deutsch.**
|
||||
//
|
||||
// Die Vorgabe von ASP.NET antwortet mit „An error occurred while processing
|
||||
// your request." — englisch, ohne Grund, ohne nächsten Schritt. Genau das
|
||||
// stand im Zuordnungsdialog, als eine Kennung doppelt vergeben werden
|
||||
// sollte. Hier bekommt jede Ausnahme, die es bis nach oben schafft, einen
|
||||
// deutschen Satz; die Einzelheiten gehen ins Tagesprotokoll, wo man sie
|
||||
// sucht.
|
||||
app.UseExceptionHandler(zweig => zweig.Run(async zusammenhang =>
|
||||
{
|
||||
var fehler = zusammenhang.Features
|
||||
.Get<Microsoft.AspNetCore.Diagnostics.IExceptionHandlerFeature>()?.Error;
|
||||
|
||||
if (fehler is not null)
|
||||
{
|
||||
zusammenhang.RequestServices.GetRequiredService<Tagebuch>().Schreiben(
|
||||
$"{zusammenhang.Request.Method} {zusammenhang.Request.Path}: "
|
||||
+ Fehlertext.FuersProtokoll(fehler),
|
||||
Tagebuch.Art.Fehler);
|
||||
}
|
||||
|
||||
zusammenhang.Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||
await Results.Problem(
|
||||
fehler is null ? "Etwas ist schiefgegangen." : Fehlertext.FuerMenschen(fehler),
|
||||
statusCode: StatusCodes.Status500InternalServerError)
|
||||
.ExecuteAsync(zusammenhang);
|
||||
}));
|
||||
app.UseStatusCodePages();
|
||||
app.UseRateLimiter();
|
||||
|
||||
|
||||
@@ -354,6 +354,53 @@ public class MetadataTests
|
||||
"Gesucht wird mit 2016 aus dem Dateinamen, nicht mit 2022 aus dem Treffer.");
|
||||
}
|
||||
|
||||
/// <summary>Zwei Dateien auf denselben Film zu legen ist kein Fehler.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Aus dem echten Bestand.</b> Im Zuordnungsdialog stand plötzlich „An
|
||||
/// error occurred while processing your request." — englisch, ohne Grund.
|
||||
/// Der gewählte Film war in derselben Mediathek schon vergeben, der
|
||||
/// Eindeutigkeitsindex schlug zu, und die Ausnahme ging ungefiltert nach
|
||||
/// oben.
|
||||
///
|
||||
/// Es ist aber gar kein Fehler: zwei Dateien, die denselben Film meinen,
|
||||
/// gehören zusammengelegt — genau das tut der Scan am Ende jedes Laufs.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task Zwei_Dateien_auf_denselben_Film_werden_zusammengelegt()
|
||||
{
|
||||
Datei("Breaking Dawn Teil 1 (2011).mkv");
|
||||
Datei("Breaking Dawn (2011).mkv");
|
||||
|
||||
// Beim Scan findet keiner etwas — so bleiben es zwei Einträge.
|
||||
var anbieter = new VorgegebenerAnbieter { Film = 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");
|
||||
Assert.That(filme!.Total, Is.EqualTo(2), "Zwei Dateien, zwei Einträge ohne Zuordnung.");
|
||||
|
||||
anbieter.Film = new MovieMetadata(50619, "Breaking Dawn – Teil 1", 2011, null, null, null)
|
||||
{
|
||||
Language = "de-DE",
|
||||
};
|
||||
|
||||
var erste = await client.PutAsJsonAsync(
|
||||
$"/api/v1/movies/{filme.Items[0].Id}/match", new ApplyMatchRequest(50619));
|
||||
Assert.That(erste.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
|
||||
var zweite = await client.PutAsJsonAsync(
|
||||
$"/api/v1/movies/{filme.Items[1].Id}/match", new ApplyMatchRequest(50619));
|
||||
Assert.That(zweite.StatusCode, Is.EqualTo(HttpStatusCode.OK),
|
||||
"Die zweite Zuordnung darf nicht scheitern.");
|
||||
|
||||
var danach = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
||||
Assert.That(danach!.Total, Is.EqualTo(1), "Ein Film.");
|
||||
Assert.That(danach.Items[0].Files, Has.Count.EqualTo(2), "Mit beiden Dateien.");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Ein_Film_ohne_Treffer_behaelt_seinen_Dateinamen()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user