Der Anlass für diesen Server, jetzt als Code. Fortschritt, Gesehen-Status, Weiterschauen, Nächste Folge, Favoriten und Bewertungen — alles je Benutzer. **Die Jellyfin-Werte sind jetzt belegt statt behauptet.** Das Entwurfsdokument hatte das ausdrücklich offengelassen („aus dem Gedächtnis"); hier stehen sie aus `MediaBrowser.Model/Configuration/ServerConfiguration.cs` und `Emby.Server.Implementations/Library/UserDataManager.cs`: | | Vorgabe | Was passiert | |---|---|---| | `MinResumePct` | 5 | darunter `positionTicks = 0` — Kommentar im Quelltext: „ignore progress during the beginning" | | `MaxResumePct` | 90 | darüber gesehen, Position **auf 0** | | `MinResumeDurationSeconds` | 300 | alles unter fünf Minuten gilt nach dem ersten Lebenszeichen als gesehen | Bei einem Zweistünder sind die ersten sechs Minuten damit weg. Eine vierminütige Folge gilt nach dreissig Sekunden als vollständig gesehen. Und fehlt die Laufzeit, nimmt Jellyfin an, der Titel sei durch — wörtlich: „we'll just have to assume it was fully played". **Flaro legt jede Meldung ab und wirft nie eine Position weg.** Eine Grenze fürs Häkchen gibt es trotzdem — 90 %, sonst stünde jeder zu Ende geschaute Film für immer bei 98 % in Weiterschauen. Der Unterschied ist nicht, *dass* es die Grenze gibt, sondern was sie anrichtet: hier wird ein Häkchen gesetzt, dort zusätzlich die Stelle vernichtet. Wer einen gesehenen Film noch einmal anfängt, hat beides — das Häkchen und den Punkt, an dem er steht. Ohne bekannte Laufzeit wird nichts vermutet. Das ist der Fehler, den man nicht zurücknehmen kann, ohne ihn zu bemerken. --- **Eine Spalte statt einer zweiten Regel.** „Gehört in Weiterschauen" vergleicht die Stelle mit einem Anteil der Laufzeit, und diese Rechnung lässt sich nicht nach SQL übersetzen. Die Regel daneben noch einmal als Abfrage zu schreiben wäre die Variante, bei der beide irgendwann auseinanderlaufen — unbemerkt, weil keine von beiden dabei falsch aussieht. Stattdessen hält `InProgress` das Ergebnis fest, geschrieben an genau einer Stelle. Eine Prüfung hält Reihe und Regel über vier Stände gegeneinander. **„Weiterschauen" und „Nächste Folge" überschneiden sich nicht.** Was angefangen ist, steht links; was ansteht, rechts. Jellyfin zeigt beides doppelt, und auf einer Startseite mit zwei Reihen nebeneinander sieht das nach einem Fehler aus. Dazu, weil die App sie braucht: Einzelabruf für Film und Serie, die nächste Folge nach einer gegebenen, und der Zustand hängt an jeder Liste mit dran — in einer Abfrage je Seite, nicht in einer je Titel. Ein Titel wird über seine Kennung allein angesprochen, ohne dass der Aufrufer sagen muss, ob es ein Film oder eine Folge ist. Die App hat diese Auskunft an der Stelle nicht. 348 Tests grün. Im Container nachgewiesen: fünf Minuten „Der Untergang" von 2:36 bleiben stehen, bei Minute 150 kommt das Häkchen und die Stelle bleibt, beim zweiten Anlauf steht der Film mit beidem wieder in der Reihe — und nach Folge 1 von Dark zeigt „Nächste Folge" S01E02. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
465 lines
18 KiB
C#
465 lines
18 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using Flaro.Api.Endpoints;
|
|
using Flaro.Core.Playback;
|
|
|
|
namespace Flaro.Api.Tests;
|
|
|
|
/// <summary>Fortschritt, Gesehen-Status und die Startseite — je Benutzer.</summary>
|
|
public class PlaybackTests
|
|
{
|
|
private string _bestand = "";
|
|
|
|
[SetUp]
|
|
public void Anlegen() => _bestand = Directory.CreateDirectory(
|
|
Path.Combine(Path.GetTempPath(), "flaro-medien-" + Guid.CreateVersion7().ToString("N"))).FullName;
|
|
|
|
[TearDown]
|
|
public void Aufraeumen()
|
|
{
|
|
try { Directory.Delete(_bestand, recursive: true); } catch (IOException) { }
|
|
}
|
|
|
|
private void Datei(string relativ)
|
|
{
|
|
var voll = Path.Combine(_bestand, relativ);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(voll)!);
|
|
File.WriteAllBytes(voll, new byte[2048]);
|
|
}
|
|
|
|
/// <summary>Legt eine Bibliothek an, scannt sie und meldet den Client.</summary>
|
|
private async Task<HttpClient> Eingelesen(FlaroServer server, string art = "Movies")
|
|
{
|
|
var admin = await server.SetUpAndSignIn();
|
|
var client = server.AlsAngemeldeter(admin.Token);
|
|
|
|
var angelegt = await client.PostAsJsonAsync("/api/v1/libraries",
|
|
new CreateLibraryRequest("Prüfbestand", art, _bestand));
|
|
angelegt.EnsureSuccessStatusCode();
|
|
var bibliothek = (await angelegt.Content.ReadFromJsonAsync<LibraryResponse>())!;
|
|
|
|
(await client.PostAsync($"/api/v1/libraries/{bibliothek.Id}/scan", null)).EnsureSuccessStatusCode();
|
|
for (var versuch = 0; versuch < 100; versuch++)
|
|
{
|
|
var stand = await client.GetFromJsonAsync<ScanStatus>($"/api/v1/libraries/{bibliothek.Id}/scan");
|
|
if (stand!.State is "finished") return client;
|
|
if (stand.State is "failed") Assert.Fail($"Der Scan ist gescheitert: {stand.Error}");
|
|
await Task.Delay(50);
|
|
}
|
|
Assert.Fail("Der Scan wurde in fünf Sekunden nicht fertig.");
|
|
return null!;
|
|
}
|
|
|
|
private static async Task<Guid> ErsterFilm(HttpClient client)
|
|
{
|
|
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
|
return seite!.Items[0].Id;
|
|
}
|
|
|
|
private static Task<HttpResponseMessage> Melden(HttpClient c, Guid id, double stelle, double? laenge) =>
|
|
c.PostAsJsonAsync($"/api/v1/items/{id}/progress", new ProgressRequest(stelle, laenge));
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
/// <summary>Der Fall, um dessentwillen der Server existiert.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Jellyfin verwirft bei zwei Stunden alles unterhalb von sechs Minuten —
|
|
/// <c>MinResumePct</c> steht auf 5. Hier kommt die Stelle zurück.
|
|
/// </remarks>
|
|
[Test]
|
|
public async Task Fuenf_Minuten_von_zwei_Stunden_bleiben_erhalten()
|
|
{
|
|
Datei("Der Untergang (2004)/Der Untergang (2004).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
var antwort = await Melden(client, film, 300, 7200);
|
|
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(stand.PositionSeconds, Is.EqualTo(300));
|
|
Assert.That(stand.Watched, Is.False);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Eine_Sekunde_wird_schon_gemerkt()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 1, 7200);
|
|
|
|
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
|
Assert.That(seite!.Items[0].State!.PositionSeconds, Is.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Kurz_vor_Schluss_gilt_er_als_gesehen_und_behaelt_die_Stelle()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
var antwort = await Melden(client, film, 7000, 7200);
|
|
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(stand.Watched, Is.True);
|
|
Assert.That(stand.PositionSeconds, Is.EqualTo(7000));
|
|
Assert.That(stand.PlayCount, Is.EqualTo(1));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Angefangenes_steht_in_Weiterschauen()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 600, 7200);
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
|
|
Assert.That(reihe.Items[0].Title, Is.EqualTo("Matrix"));
|
|
Assert.That(reihe.Items[0].Kind, Is.EqualTo("movie"));
|
|
Assert.That(reihe.Items[0].State.PositionSeconds, Is.EqualTo(600));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Durchgeschautes_verlaesst_Weiterschauen()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 600, 7200);
|
|
await Melden(client, film, 7000, 7200);
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
Assert.That(reihe!.Items, Is.Empty);
|
|
}
|
|
|
|
/// <summary>Ein zweites Mal angefangen — mit Häkchen und Stelle zugleich.</summary>
|
|
[Test]
|
|
public async Task Ein_gesehener_Film_kommt_beim_zweiten_Anlauf_zurueck()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 7000, 7200);
|
|
await Melden(client, film, 120, 7200);
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
|
|
Assert.That(reihe.Items[0].State.Watched, Is.True, "gesehen hat er ihn ja");
|
|
Assert.That(reihe.Items[0].State.PositionSeconds, Is.EqualTo(120));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Weglegen_nimmt_aus_der_Reihe_ohne_gesehen_zu_behaupten()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 1800, 7200);
|
|
(await client.PostAsync($"/api/v1/items/{film}/dismiss", null)).EnsureSuccessStatusCode();
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
var seite = await client.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(reihe!.Items, Is.Empty);
|
|
Assert.That(seite!.Items[0].State!.Watched, Is.False, "weggelegt heisst nicht gesehen");
|
|
Assert.That(seite.Items[0].State!.PositionSeconds, Is.EqualTo(1800), "die Stelle bleibt");
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Wer_doch_weiterschaut_holt_es_zurueck()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 1800, 7200);
|
|
await client.PostAsync($"/api/v1/items/{film}/dismiss", null);
|
|
await Melden(client, film, 1900, 7200);
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Von_Hand_als_gesehen_raeumt_die_Stelle_weg()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await Melden(client, film, 1800, 7200);
|
|
var antwort = await client.PutAsJsonAsync($"/api/v1/items/{film}/watched", new WatchedRequest(true));
|
|
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(stand.Watched, Is.True);
|
|
Assert.That(stand.PositionSeconds, Is.Zero);
|
|
Assert.That(reihe!.Items, Is.Empty);
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Favorit_und_Bewertung_bleiben_stehen()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
await client.PutAsJsonAsync($"/api/v1/items/{film}/favourite", new FavouriteRequest(true));
|
|
await client.PutAsJsonAsync($"/api/v1/items/{film}/rating", new RatingRequest(9));
|
|
|
|
var einzeln = await client.GetFromJsonAsync<MovieResponse>($"/api/v1/movies/{film}");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(einzeln!.State!.Favourite, Is.True);
|
|
Assert.That(einzeln.State!.Rating, Is.EqualTo(9));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Eine_Bewertung_ausserhalb_von_eins_bis_zehn_wird_abgelehnt()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
var antwort = await client.PutAsJsonAsync($"/api/v1/items/{film}/rating", new RatingRequest(11));
|
|
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
|
|
}
|
|
|
|
/// <summary>Zwei Leute im selben Haushalt stehen an verschiedenen Stellen.</summary>
|
|
[Test]
|
|
public async Task Der_Stand_gehoert_dem_Benutzer_nicht_dem_Server()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var admin = await Eingelesen(server);
|
|
var film = await ErsterFilm(admin);
|
|
|
|
(await admin.PostAsJsonAsync("/api/v1/users",
|
|
new CreateUserRequest("mitbewohner", "ein-langes-passwort", false))).EnsureSuccessStatusCode();
|
|
var zweiter = server.AlsAngemeldeter(
|
|
(await server.SignIn("mitbewohner", "ein-langes-passwort", "iPad", "geraet-2")).Token);
|
|
|
|
await Melden(admin, film, 600, 7200);
|
|
await Melden(zweiter, film, 3000, 7200);
|
|
|
|
var seiteA = await admin.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
|
var seiteB = await zweiter.GetFromJsonAsync<Page<MovieResponse>>("/api/v1/movies");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(seiteA!.Items[0].State!.PositionSeconds, Is.EqualTo(600));
|
|
Assert.That(seiteB!.Items[0].State!.PositionSeconds, Is.EqualTo(3000));
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Einen_Titel_den_es_nicht_gibt_kann_man_nicht_melden()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
|
|
var antwort = await Melden(client, Guid.CreateVersion7(), 60, 7200);
|
|
Assert.That(antwort.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
|
|
}
|
|
|
|
/// <summary>Ohne Angabe nimmt der Server die Laufzeit aus der Datei.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// Die Prüfdateien haben keine lesbare Laufzeit — ffprobe findet nichts in
|
|
/// zweitausend Nullbytes. Ohne Laufzeit darf nichts vermutet werden, und
|
|
/// genau das wird hier festgehalten.
|
|
/// </remarks>
|
|
[Test]
|
|
public async Task Ohne_Laufzeit_wird_nichts_als_gesehen_vermutet()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
var antwort = await Melden(client, film, 99999, null);
|
|
var stand = (await antwort.Content.ReadFromJsonAsync<ItemStateResponse>())!;
|
|
|
|
Assert.That(stand.Watched, Is.False);
|
|
}
|
|
|
|
// --- Serien --------------------------------------------------------
|
|
|
|
[Test]
|
|
public async Task Ungesehene_Folgen_werden_gezaehlt()
|
|
{
|
|
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
|
|
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
|
|
Datei("Dark/Season 01/Dark - S01E03 - Gestern und heute.mkv");
|
|
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server, "Shows");
|
|
|
|
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
|
|
var serie = serien!.Items[0];
|
|
Assert.That(serie.UnwatchedCount, Is.EqualTo(3));
|
|
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>($"/api/v1/series/{serie.Id}/episodes");
|
|
await client.PutAsJsonAsync($"/api/v1/items/{folgen!.Items[0].Id}/watched", new WatchedRequest(true));
|
|
|
|
var danach = await client.GetFromJsonAsync<SeriesResponse>($"/api/v1/series/{serie.Id}");
|
|
Assert.That(danach!.UnwatchedCount, Is.EqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public async Task Die_naechste_Folge_richtet_sich_nach_Staffel_und_Nummer()
|
|
{
|
|
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
|
|
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
|
|
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server, "Shows");
|
|
|
|
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
|
|
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
|
|
|
|
var naechste = await client.GetFromJsonAsync<EpisodeResponse>(
|
|
$"/api/v1/episodes/{folgen!.Items[0].Id}/next");
|
|
var hinterDerLetzten = await client.GetAsync($"/api/v1/episodes/{folgen.Items[1].Id}/next");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(naechste!.Episode, Is.EqualTo(2));
|
|
Assert.That(hinterDerLetzten.StatusCode, Is.EqualTo(HttpStatusCode.NoContent),
|
|
"nach der letzten Folge kommt keine — das ist kein Fehler");
|
|
});
|
|
}
|
|
|
|
[Test]
|
|
public async Task Die_erste_ungesehene_Folge_steht_unter_Naechste_Folge()
|
|
{
|
|
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
|
|
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
|
|
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server, "Shows");
|
|
|
|
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
|
|
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
|
|
|
|
await client.PutAsJsonAsync($"/api/v1/items/{folgen!.Items[0].Id}/watched", new WatchedRequest(true));
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/next");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(reihe!.Items, Has.Count.EqualTo(1));
|
|
Assert.That(reihe.Items[0].Episode, Is.EqualTo(2));
|
|
Assert.That(reihe.Items[0].Title, Is.EqualTo("Dark"), "oben die Serie");
|
|
});
|
|
}
|
|
|
|
/// <summary>Was in „Weiterschauen" steht, steht nicht nochmal daneben.</summary>
|
|
[Test]
|
|
public async Task Eine_angefangene_Folge_erscheint_nur_in_einer_Reihe()
|
|
{
|
|
Datei("Dark/Season 01/Dark - S01E01 - Geheimnisse.mkv");
|
|
Datei("Dark/Season 01/Dark - S01E02 - Luegen.mkv");
|
|
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server, "Shows");
|
|
|
|
var serien = await client.GetFromJsonAsync<Page<SeriesResponse>>("/api/v1/series");
|
|
var folgen = await client.GetFromJsonAsync<Page<EpisodeResponse>>(
|
|
$"/api/v1/series/{serien!.Items[0].Id}/episodes");
|
|
|
|
await Melden(client, folgen!.Items[0].Id, 400, 2700);
|
|
|
|
var weiter = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
var naechste = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/next");
|
|
|
|
Assert.Multiple(() =>
|
|
{
|
|
Assert.That(weiter!.Items, Has.Count.EqualTo(1));
|
|
Assert.That(weiter.Items[0].Episode, Is.EqualTo(1));
|
|
Assert.That(naechste!.Items, Is.Empty);
|
|
});
|
|
}
|
|
|
|
/// <summary>Die Spalte und die Regel dürfen nicht auseinanderlaufen.</summary>
|
|
///
|
|
/// <remarks>
|
|
/// <c>InProgress</c> ist das festgehaltene Ergebnis von
|
|
/// <see cref="ProgressRule.BelongsInContinueWatching"/>. Wer die Regel
|
|
/// ändert und das Nachführen vergisst, bekommt eine Startseite, die lügt —
|
|
/// und zwar lautlos. Deshalb hier gegeneinandergehalten.
|
|
/// </remarks>
|
|
[Test]
|
|
public async Task Die_Reihe_zeigt_genau_das_was_die_Regel_sagt()
|
|
{
|
|
Datei("Matrix (1999)/Matrix (1999).mkv");
|
|
using var server = new FlaroServer();
|
|
var client = await Eingelesen(server);
|
|
var film = await ErsterFilm(client);
|
|
|
|
foreach (var (stelle, laenge) in new (double, double?)[]
|
|
{ (1, 7200), (300, 7200), (7000, 7200), (120, 7200) })
|
|
{
|
|
await Melden(client, film, stelle, laenge);
|
|
|
|
var erwartet = ProgressRule.BelongsInContinueWatching(new UserItemState
|
|
{
|
|
UserId = Guid.Empty,
|
|
MovieId = film,
|
|
Position = TimeSpan.FromSeconds(stelle),
|
|
Runtime = laenge is { } l ? TimeSpan.FromSeconds(l) : null,
|
|
});
|
|
|
|
var reihe = await client.GetFromJsonAsync<Page<ContinueEntry>>("/api/v1/home/continue");
|
|
Assert.That(reihe!.Items.Count == 1, Is.EqualTo(erwartet),
|
|
$"bei {stelle}s von {laenge}s");
|
|
}
|
|
}
|
|
}
|