„Hinterlegt" war keine Auskunft, und die Übersicht keine Ablage

**Man konnte nicht sehen, ob ein Schlüssel wirkt.** Nach dem Eintragen
stand „Hinterlegt" da, und das war alles. Ob er stimmt, ob der Dienst
antwortet, ob zu einem Film überhaupt etwas zu holen ist — herausfinden
liess sich das nur, indem man die Metadaten über achthundert Titel
auffrischte und danach nachsah, ob sich etwas geändert hatte.

Jetzt gibt es „Verbindung prüfen". Es wird mit „The Matrix" gefragt, nicht
mit einem Film aus der Mediathek: der könnte schlicht unbekannt sein, und
dann wüsste man wieder nicht, ob es am Schlüssel liegt oder am Film. Die
Antwort nennt, was zurückkam — bei fanart.tv die Zahl der Plakate und
davon die in der eingestellten Sprache, denn genau dafür ist die Quelle da.

Gemessen am laufenden Server:

    tmdb    eingerichtet  ok   Erreichbar — Probeabruf ergab The Matrix (1999).
    fanart  —             —    Kein Schlüssel hinterlegt — die Quelle ist aus.
    omdb    —             —    Kein Schlüssel hinterlegt — die Quelle ist aus.

**Und die Übersicht war eine Ablage für alles geworden.** Zu Recht
angemerkt: es gibt eine Seite „Metadaten", und die Zugänge standen trotzdem
unter „Übersicht" — zwischen Servername, doppelten Einträgen, Fassung und
Speicherplatz. Die drei Quellen stehen jetzt dort, wo man sie sucht, jede
mit einem Satz dazu, was sie liefert und was sie dafür braucht.

Aufmacherhöhe und saisonale Reihen bekommen einen eigenen Bereich
„Darstellung". Eine Seite, die „Übersicht" heisst, ist nicht der Ort für
alles, was sonst nirgends passt.

**Am Telefon tat die Aufmacherhöhe gar nichts.** Gemeldet als „auf 100 %
ist es immer noch zu niedrig" — und das stimmte buchstäblich: unter 860
Pixeln Breite stand `height: min(52vh, 420px)` fest und überschrieb die
Einstellung vollständig. Ob „niedrig" oder „sehr hoch", es kamen immer
dieselben 52 vh heraus.

Jetzt hat jede Stufe zwei Höhen, eine fürs breite und eine fürs schmale
Bild; am Telefon fällt sie eine Spur flacher aus, weil der Bildschirm
hochkant ist und derselbe Anteil der ersten Kachelreihe dort den Platz
nähme. Dazu eine fünfte Stufe „Bildschirmhoch" — mehr ginge nicht
sinnvoll, darüber ist es kein Aufmacher mehr, sondern die ganze Seite.

Eine Prüfung hält ausserdem fest, dass ein Film ohne passendes Stichwort
und ohne passendes Genre nicht in der saisonalen Reihe steht. Anlass war
die Meldung, dort stünden harmlose Zeichentrickfilme — die Reihe arbeitet
richtig, sie macht nur sichtbar, wenn ein Titel auf den falschen Eintrag
zugeordnet ist.

765 Tests grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-08 22:11:55 +02:00
co-authored by Claude Opus 5
parent e066f5fe87
commit 5dfc22bdce
11 changed files with 483 additions and 160 deletions
+6 -1
View File
@@ -26,7 +26,12 @@ public static class Aufmacherhoehe
/// <summary>Die Mitte — das, was vorher fest verdrahtet war.</summary>
public const string Vorgabe = "mittel";
private static readonly string[] Stufen = ["niedrig", "mittel", "hoch", "sehrhoch"];
/// <remarks>
/// <b>Die fünfte kam nach.</b> Am Telefon war selbst „sehr hoch" zu
/// flach — dort überschrieb eine feste Regel die Einstellung ganz. Das
/// ist behoben; die zusätzliche Stufe gibt darüber hinaus Luft.
/// </remarks>
private static readonly string[] Stufen = ["niedrig", "mittel", "hoch", "sehrhoch", "voll"];
/// <summary>Macht aus dem Gespeicherten eine der vier Stufen.</summary>
///
@@ -164,6 +164,17 @@ public static class SettingsEndpoints
Kodierstufen.Lesen(anfrage.Encoder).AlsWort(), geaendert));
}).RequireAuthorization(FlaroPolicies.Administrator);
// **Die Quellen einmal fragen und sagen, was zurückkam.**
//
// Vorher stand nach dem Eintragen eines Schlüssels nur „Hinterlegt"
// da. Ob er stimmt und ob der Dienst antwortet, liess sich nur
// herausfinden, indem man die Metadaten über achthundert Titel
// auffrischte und danach nachsah, ob sich etwas geändert hat.
app.MapPost("/api/v1/settings/metadata/test", async (
Quellenprobe probe, CancellationToken ct) =>
Results.Ok(await probe.LaufenAsync(ct)))
.RequireAuthorization(FlaroPolicies.Administrator);
/* ---------- Saisonale Reihen ---------- */
// **Einstellbar und nicht eingebaut.** Halloween und Weihnachten
+4
View File
@@ -124,6 +124,10 @@ builder.Services.AddHttpClient<OmdbClient>(c =>
c.Timeout = TimeSpan.FromSeconds(20);
});
builder.Services.AddScoped<IBewertungsquelle>(sp => sp.GetRequiredService<OmdbClient>());
// **Damit man sieht, ob ein Schlüssel wirkt.** „Hinterlegt" ist keine
// Auskunft darüber, ob der Dienst antwortet.
builder.Services.AddScoped<Quellenprobe>();
builder.Services.AddSingleton(new ImageCacheOptions(pfade.Cache));
builder.Services.AddHttpClient<ImageCache>(c => c.Timeout = TimeSpan.FromSeconds(30));
builder.Services.AddScoped<IImageStore>(sp => sp.GetRequiredService<ImageCache>());
+112
View File
@@ -0,0 +1,112 @@
using Flaro.Core.Metadata;
using Flaro.Metadata;
namespace Flaro.Api;
/// <summary>Was eine Quelle auf Nachfrage geantwortet hat.</summary>
///
/// <param name="Source">„tmdb", „fanart" oder „omdb".</param>
/// <param name="Configured">Ob überhaupt ein Zugang hinterlegt ist.</param>
/// <param name="Ok">Ob die Probe etwas Brauchbares ergeben hat.</param>
/// <param name="Detail">Ein Satz, der sagt, was herauskam.</param>
public sealed record Quellenbefund(string Source, bool Configured, bool Ok, string Detail);
/// <summary>Fragt die Quellen einmal und sagt, was zurückkam.</summary>
///
/// <remarks>
/// <para>
/// <b>Der Anlass.</b> Nach dem Eintragen eines Schlüssels stand in der
/// Oberfläche „Hinterlegt" — und das war alles. Ob er stimmt, ob der Dienst
/// antwortet und ob zu einem Film überhaupt etwas zu holen ist, liess sich
/// nicht feststellen; man trug ein, frischte die Metadaten über
/// achthundert Titel auf und sah danach nach, ob sich etwas geändert hatte.
/// </para>
/// <para>
/// <b>Mit einem bekannten Film, nicht mit einem aus der Mediathek.</b> „The
/// Matrix" hat bei allen drei Quellen Einträge; ein Film aus dem eigenen
/// Bestand könnte schlicht unbekannt sein, und dann wüsste man wieder
/// nicht, ob es am Schlüssel liegt oder am Film.
/// </para>
/// </remarks>
public sealed class Quellenprobe(
TmdbClient tmdb,
FanartClient fanart,
OmdbClient omdb,
ITmdbSettingsSource spracheVon)
{
/// <summary>„The Matrix" — bei allen drei Quellen vorhanden.</summary>
private const int ProbeTmdbId = 603;
private const string ProbeImdbId = "tt0133093";
public async Task<IReadOnlyList<Quellenbefund>> LaufenAsync(CancellationToken ct = default) =>
[
await TmdbProbe(ct),
await FanartProbe(ct),
await OmdbProbe(ct),
];
private async Task<Quellenbefund> TmdbProbe(CancellationToken ct)
{
if (!await tmdb.IsConfiguredAsync(ct))
return new("tmdb", false, false, "Kein Zugang hinterlegt.");
var film = await tmdb.FindMovieAsync("The Matrix", 1999, ct);
return film is null
? new("tmdb", true, false,
"Der Dienst hat nicht geantwortet oder den Zugang abgelehnt.")
: new("tmdb", true, true,
$"Erreichbar — Probeabruf ergab {film.Title} ({film.Year}).");
}
private async Task<Quellenbefund> FanartProbe(CancellationToken ct)
{
if (!await fanart.EingerichtetAsync(ct))
return new("fanart", false, false, "Kein Schlüssel hinterlegt — die Quelle ist aus.");
var bilder = await fanart.FilmbilderAsync(ProbeTmdbId, ct);
var plakate = bilder.Where(b => b.Kind == "poster").ToList();
if (plakate.Count == 0)
{
return new("fanart", true, false,
"Erreicht, aber nichts bekommen. Stimmt der Schlüssel?");
}
// **Die Zahl in der eingestellten Sprache ist die eigentliche
// Auskunft.** „42 Plakate" sagt nur, dass etwas kam; „davon 3
// deutsche" sagt, ob die Quelle den Zweck erfüllt, für den sie da ist.
var sprache = (await spracheVon.GetAsync(ct)).Language.Split('-')[0];
var passend = plakate.Count(b =>
string.Equals(b.Language, sprache, StringComparison.OrdinalIgnoreCase));
return new("fanart", true, true,
$"Erreichbar — {plakate.Count} Plakate zu The Matrix, davon {passend} in {sprache}.");
}
private async Task<Quellenbefund> OmdbProbe(CancellationToken ct)
{
if (!await omdb.EingerichtetAsync(ct))
return new("omdb", false, false, "Kein Schlüssel hinterlegt — die Quelle ist aus.");
var wertungen = await omdb.HolenAsync(ProbeImdbId, ct);
if (wertungen.Count == 0)
{
return new("omdb", true, false,
"Erreicht, aber nichts bekommen. Stimmt der Schlüssel, "
+ "oder ist das Tageskontingent aufgebraucht?");
}
var namen = string.Join(", ", wertungen.Select(w => $"{Kurz(w.Quelle)} {w.Anzeige}"));
return new("omdb", true, true, $"Erreichbar — zu The Matrix: {namen}.");
}
private static string Kurz(string quelle) => quelle switch
{
"imdb" => "IMDb",
"rottenTomatoes" => "RT",
"metacritic" => "MC",
_ => quelle,
};
}