Eine zweite Bildquelle, weil TMDB nicht alles hat
„365 Tage" stand mit polnischem Plakat da. Nachgesehen: TMDB hat zu diesem Film 26 Plakate und kein einziges deutsches. Die Auswahl von gestern nimmt seither das sprachlose statt des polnischen — erfinden kann sie keines. fanart.tv ist die Quelle, bei der jedes Plakat seine Sprache mitführt, und die Gemeinschaft dort pflegt landessprachliche Fassungen. OMDb wäre keine Hilfe gewesen: es liefert das Plakat von IMDb, und das ist in aller Regel das amerikanische. **Ausdrücklich keine zweite Metadatenquelle.** `IBildquelle` liefert Bilder, sonst nichts. Titel, Beschreibung und Besetzung kommen weiterhin von einer Stelle — zwei Anbieter für dieselbe Angabe wären zwei Wahrheiten. **Gefragt wird nur, wenn es lohnt.** `MovieMetadata` trägt jetzt die Sprache des gewählten Plakats mit. Hat TMDB schon eines in der eingestellten Sprache, geht keine Anfrage hinaus — bei achthundert Titeln ist das der Unterschied zwischen „läuft nebenher" und „hängt am fremden Dienst". Und nur ein landessprachliches sticht: ein sprachloses von fanart.tv ist nicht besser als ein sprachloses von TMDB. **Die Rangfolge liegt jetzt im Kern**, nicht im TMDB-Client. Sie gilt für alle Quellen, sonst kämen die Bilder zweier Anbieter in zwei Reihenfolgen zusammen. Im Bilderdialog stehen beide Quellen gemeinsam geordnet: ein deutsches Plakat von fanart.tv vor einem sprachlosen von TMDB. **Nur Filme.** Der Serienweg von fanart.tv verlangt eine TVDB-Kennung, und die führt Flaro nicht. Eine halbe Lösung, die bei Serien stillschweigend nichts tut, wäre schlechter als eine, die sagt, worauf sie sich beschränkt. **Kein mitgelieferter Schlüssel.** Bei TMDB gibt es einen als Rückfall; fanart.tv verlangt einen persönlichen, und einen fremden mitzuliefern hiesse, ihn fremd zu benutzen. Ohne eigenen Schlüssel geht keine Anfrage hinaus — geprüft. Einzutragen unter Einstellungen → Übersicht → Metadaten. Dass „00" dort „ohne Sprache" heisst, wird beim Lesen übersetzt: sonst führte die gemeinsame Rangfolge ein sprachloses Plakat als Sprache „00" und setzte es hinter Englisch statt davor. 732 Tests grün, davon sieben neue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2bd13f0f19
commit
97edb49564
@@ -177,7 +177,7 @@ public sealed class WartungsplanTests
|
||||
Assert.That(stand!.Select(s => s.Aufgabe),
|
||||
Is.EquivalentTo(new[] { "Sicherung", "Optimieren", "Bilder" }));
|
||||
Assert.That(stand!.First(s => s.Aufgabe == "Sicherung").Tage, Is.EqualTo(3));
|
||||
Assert.That(stand.All(s => s.Zuletzt is null), Is.True, "noch lief keine");
|
||||
Assert.That(stand!.All(s => s.Zuletzt is null), Is.True, "noch lief keine");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
using System.Net;
|
||||
using Flaro.Core.Metadata;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace Flaro.Metadata.Tests;
|
||||
|
||||
/// <summary>Plakate von fanart.tv — der Quelle, bei der die Sprache mitsteht.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Der Anlass.</b> „365 Tage" stand mit polnischem Plakat da. Nachgesehen:
|
||||
/// TMDB hat zu diesem Film 26 Plakate und kein einziges deutsches. Die
|
||||
/// Auswahl im TMDB-Client nimmt seither das sprachlose statt des polnischen
|
||||
/// — erfinden kann sie ein deutsches nicht. Genau dafür gibt es diese zweite
|
||||
/// Quelle.
|
||||
/// </remarks>
|
||||
public class FanartTests
|
||||
{
|
||||
private static FanartClient Client(Attrappe attrappe, string? schluessel = "ein-schluessel") =>
|
||||
new(new HttpClient(attrappe) { BaseAddress = new Uri(FanartClient.BaseAddress) },
|
||||
new FesterFanartSchluessel(schluessel),
|
||||
NullLogger<FanartClient>.Instance);
|
||||
|
||||
private const string Antwort = """
|
||||
{"name":"Probe","tmdb_id":"500",
|
||||
"movieposter":[
|
||||
{"id":"1","url":"https://assets.fanart.tv/de.jpg","lang":"de","likes":"3"},
|
||||
{"id":"2","url":"https://assets.fanart.tv/en.jpg","lang":"en","likes":"9"},
|
||||
{"id":"3","url":"https://assets.fanart.tv/ohne.jpg","lang":"00","likes":"5"}],
|
||||
"moviebackground":[
|
||||
{"id":"4","url":"https://assets.fanart.tv/hg.jpg","lang":"","likes":"2"}]}
|
||||
""";
|
||||
|
||||
[Test]
|
||||
public async Task Ohne_Schluessel_geht_keine_Anfrage_hinaus()
|
||||
{
|
||||
var attrappe = Attrappe.MitJson(Antwort);
|
||||
|
||||
var bilder = await Client(attrappe, schluessel: null).BilderAsync("500");
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bilder, Is.Empty);
|
||||
Assert.That(attrappe.Aufrufe, Is.Empty,
|
||||
"fanart.tv verlangt einen persönlichen Schlüssel — ohne ihn wird nicht gefragt");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Die_Plakate_kommen_mit_ihrer_Sprache()
|
||||
{
|
||||
var bilder = await Client(Attrappe.MitJson(Antwort)).BilderAsync("500");
|
||||
var plakate = bilder.Where(b => b.Kind == "poster").ToList();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(plakate, Has.Count.EqualTo(3));
|
||||
Assert.That(plakate.Select(p => p.Language), Is.EquivalentTo(new[] { "de", "en", null }));
|
||||
Assert.That(plakate.All(p => p.Quelle == "fanart"), Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>„00" ist bei fanart.tv die Schreibweise für „ohne Sprache".</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Sie durchzureichen hiesse, ein sprachloses Plakat als Sprache „00" zu
|
||||
/// führen — und die gemeinsame Rangfolge setzte es damit hinter Englisch
|
||||
/// statt davor.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task Die_Null_Null_wird_zu_sprachlos()
|
||||
{
|
||||
var bilder = await Client(Attrappe.MitJson(Antwort)).BilderAsync("500");
|
||||
|
||||
Assert.That(bilder.Any(b => b.Language == "00"), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>Zu den meisten Filmen hat fanart.tv nichts — das ist kein Fehler.</summary>
|
||||
[Test]
|
||||
public async Task Ein_unbekannter_Film_ergibt_eine_leere_Liste()
|
||||
{
|
||||
var bilder = await Client(Attrappe.MitStatus(HttpStatusCode.NotFound)).BilderAsync("500");
|
||||
|
||||
Assert.That(bilder, Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Eine hakende fremde Quelle darf keinen Scan anhalten.</summary>
|
||||
[Test]
|
||||
public async Task Kein_Netz_ergibt_nichts_statt_eines_Absturzes()
|
||||
{
|
||||
var bilder = await Client(Attrappe.DieWirft(new HttpRequestException("weg"))).BilderAsync("500");
|
||||
|
||||
Assert.That(bilder, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Der_Schluessel_steht_in_der_Anfrage()
|
||||
{
|
||||
var attrappe = Attrappe.MitJson(Antwort);
|
||||
await Client(attrappe).BilderAsync("500");
|
||||
|
||||
Assert.That(attrappe.Aufrufe[0], Does.Contain("movies/500").And.Contain("api_key=ein-schluessel"));
|
||||
}
|
||||
|
||||
/// <summary>Ein deutsches Plakat von fanart.tv sticht ein sprachloses von TMDB.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Das ist der ganze Zweck der zweiten Quelle, und die Rangfolge dafür
|
||||
/// liegt im Kern — sie muss für beide Quellen dieselbe sein, sonst kämen
|
||||
/// die Bilder zweier Anbieter in zwei Reihenfolgen zusammen.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public async Task Die_gemeinsame_Rangfolge_stellt_das_deutsche_nach_vorn()
|
||||
{
|
||||
var vonFanart = await Client(Attrappe.MitJson(Antwort)).BilderAsync("500");
|
||||
var vonTmdb = new[]
|
||||
{
|
||||
new Bildkandidat("poster", "tmdb-ohne.jpg", "t", null, 9.5),
|
||||
new Bildkandidat("poster", "tmdb-en.jpg", "t", "en", 9.9),
|
||||
};
|
||||
|
||||
var geordnet = Bildrang
|
||||
.Geordnet(vonTmdb.Concat(vonFanart.Where(b => b.Kind == "poster")), "de")
|
||||
.ToList();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(geordnet[0].Url, Does.EndWith("de.jpg"));
|
||||
Assert.That(geordnet[0].Quelle, Is.EqualTo("fanart"));
|
||||
Assert.That(geordnet[1].Url, Does.Contain("ohne"), "dann das sprachlose, egal von wem");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record FesterFanartSchluessel(string? Wert) : IFanartSettingsSource
|
||||
{
|
||||
public Task<string?> SchluesselAsync(CancellationToken ct = default) => Task.FromResult(Wert);
|
||||
}
|
||||
Reference in New Issue
Block a user