Der Publikumswert steht auf der Seite, also wird er von dort gelesen

OMDb führt im Feld `Ratings` nur eine Zahl je Dienst, und bei Rotten
Tomatoes ist das die der Kritik. Die erweiterten Felder gibt es, sie
stehen aber seit Jahren auf `N/A`; Jellyfin fragt sie an und liest sie
nicht einmal aus — deren Antwortklasse kennt `tomatoUserMeter` gar nicht.
Dort hat den Publikumswert also niemand.

Auf der Seite selbst steht er. Gemessen an `/m/the_matrix`: im Quelltext
liegt `"audienceScore":{… "score":"85" …}` neben `"criticsScore"`, dazu
ein `application/ld+json` mit Titel und Erscheinungsdatum; bei Serien
unter `/tv/…` dasselbe, und ein unbekannter Weg antwortet mit 404. Eine
Prüfung mit `[Explicit]` geht einmal wirklich hinaus und bestätigt es:
`rottenTomatoes 83%, rottenTomatoesAudience 85%`.

Das ist Auslesen einer fremden Webseite, und es hat seinen Preis. Eine
offene Suche gibt es nicht — `/napi/search` antwortet mit 401 —, also
wird der Weg aus dem Titel geraten und hinterher am Jahr geprüft: auf der
Seite steht keine IMDb-Kennung, und `/m/the_lion_king` ist der von 1994.
Weicht das Jahr um mehr als eines ab, wird verworfen; lieber keine Zahl
als die eines fremden Films. Der Originaltitel kommt zuerst dran, sonst
fände ein deutscher Bestand dort fast nichts. Ohne Jahr zuerst, weil der
Normalfall den blanken Namen trägt — mit Jahr zuerst kostete jeder Titel
eine Anfrage zu viel.

Deshalb ein Schalter, und der steht aus, bis jemand ihn umlegt. Dazu eine
Bremse: höchstens eine Anfrage je Sekunde, prozessweit. Achthundert Titel
in einem Schwall von einer Adresse wären eine Zumutung und würden
gesperrt. Der Scan dauert dadurch länger; das ist der Preis fürs
Mitbenutzen.

Die Probe unter Einstellungen → Metadaten fragt die Quelle mit, und dort
ist sie mehr als eine Höflichkeit: wird die Seite umgebaut, merkt man es
genau da und sonst nirgends.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-09 16:51:22 +02:00
co-authored by Claude Opus 5
parent e05bbfc2cd
commit 8397e042b8
16 changed files with 965 additions and 36 deletions
+15 -12
View File
@@ -14,6 +14,9 @@ namespace Flaro.Metadata.Tests;
/// </remarks>
public class OmdbTests
{
/// <summary>Wonach gefragt wird — bei OMDb zählt allein die Kennung.</summary>
private static Wertungsfrage Frage(string imdbId) => new(imdbId, "Probe", 1999);
private static OmdbClient Client(Attrappe attrappe, string? schluessel = "ein-schluessel") =>
new(new HttpClient(attrappe) { BaseAddress = new Uri(OmdbClient.BaseAddress) },
new FesterOmdbSchluessel(schluessel),
@@ -32,7 +35,7 @@ public class OmdbTests
{
var attrappe = Attrappe.MitJson(Antwort);
var wertungen = await Client(attrappe, schluessel: null).HolenAsync("tt0000001");
var wertungen = await Client(attrappe, schluessel: null).HolenAsync(Frage("tt0000001"));
Assert.Multiple(() =>
{
@@ -51,7 +54,7 @@ public class OmdbTests
[Test]
public async Task Alle_drei_Quellen_kommen_normiert_und_lesbar()
{
var wertungen = await Client(Attrappe.MitJson(Antwort)).HolenAsync("tt0000001");
var wertungen = await Client(Attrappe.MitJson(Antwort)).HolenAsync(Frage("tt0000001"));
Assert.Multiple(() =>
{
@@ -81,7 +84,7 @@ public class OmdbTests
{
var json = """{"Response":"False","Error":"Incorrect IMDb ID."}""";
Assert.That(await Client(Attrappe.MitJson(json)).HolenAsync("tt0000001"), Is.Empty);
Assert.That(await Client(Attrappe.MitJson(json)).HolenAsync(Frage("tt0000001")), Is.Empty);
}
/// <summary>Eine unbekannte vierte Quelle wird übergangen, nicht geraten.</summary>
@@ -94,7 +97,7 @@ public class OmdbTests
{"Source":"Internet Movie Database","Value":"7.4/10"}]}
""";
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync("tt0000001");
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync(Frage("tt0000001"));
Assert.That(wertungen.Select(w => w.Quelle), Is.EquivalentTo(new[] { "imdb" }));
}
@@ -103,7 +106,7 @@ public class OmdbTests
public async Task Kein_Netz_ergibt_nichts_statt_eines_Absturzes()
{
var wertungen = await Client(Attrappe.DieWirft(new HttpRequestException("weg")))
.HolenAsync("tt0000001");
.HolenAsync(Frage("tt0000001"));
Assert.That(wertungen, Is.Empty);
}
@@ -112,7 +115,7 @@ public class OmdbTests
public async Task Ein_Fehlercode_ergibt_nichts()
{
Assert.That(
await Client(Attrappe.MitStatus(HttpStatusCode.Unauthorized)).HolenAsync("tt0000001"),
await Client(Attrappe.MitStatus(HttpStatusCode.Unauthorized)).HolenAsync(Frage("tt0000001")),
Is.Empty);
}
@@ -120,7 +123,7 @@ public class OmdbTests
public async Task Kennung_und_Schluessel_stehen_in_der_Anfrage()
{
var attrappe = Attrappe.MitJson(Antwort);
await Client(attrappe).HolenAsync("tt0133093");
await Client(attrappe).HolenAsync(Frage("tt0133093"));
Assert.That(attrappe.Aufrufe[0],
Does.Contain("i=tt0133093").And.Contain("apikey=ein-schluessel"));
@@ -137,7 +140,7 @@ public class OmdbTests
public async Task Der_Publikumswert_wird_mitverlangt()
{
var attrappe = Attrappe.MitJson(Antwort);
await Client(attrappe).HolenAsync("tt0133093");
await Client(attrappe).HolenAsync(Frage("tt0133093"));
Assert.That(attrappe.Aufrufe, Has.Count.EqualTo(1));
Assert.That(attrappe.Aufrufe[0], Does.Contain("tomatoes=true"));
@@ -159,7 +162,7 @@ public class OmdbTests
"tomatoUserMeter":"88"}
""";
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync("tt0000001");
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync(Frage("tt0000001"));
Assert.Multiple(() =>
{
@@ -192,7 +195,7 @@ public class OmdbTests
"tomatoUserMeter":{{feld}}}
""";
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync("tt0000001");
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync(Frage("tt0000001"));
Assert.Multiple(() =>
{
@@ -219,7 +222,7 @@ public class OmdbTests
"tomatoUserMeter":88}
""";
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync("tt0000001");
var wertungen = await Client(Attrappe.MitJson(json)).HolenAsync(Frage("tt0000001"));
Assert.That(wertungen.Select(w => w.Quelle),
Is.EquivalentTo(new[] { "imdb", "rottenTomatoesAudience" }));
@@ -236,7 +239,7 @@ public class OmdbTests
{
var attrappe = Attrappe.MitJson(Antwort);
await Client(attrappe).HolenAsync("");
await Client(attrappe).HolenAsync(Frage(""));
Assert.That(attrappe.Aufrufe, Is.Empty);
}