Der Sprung ging ins Leere, und die Prüfung dagegen griff nie

Nach vorn ziehen, Regler springt zurück. Im Pausieren nicht — dort wuchs
die Abspielliste weiter und holte die Stelle irgendwann ein.

Der Spieler sollte einen Sprung ausserhalb des Stroms daran erkennen,
dass er hinter dessen Ende liegt. Das Ende stand in `streamEnd`, und das
wird gesetzt, wenn der Player eine Dauer meldet — was er bei einer
wachsenden Liste nie tut. `streamEnd` blieb also nichts, jeder Sprung
galt als innerhalb, AVPlayer bekam ein Ziel ohne Schnipsel und verwarf
es.

Gefragt wird jetzt der Vorrat: so weit, wie Daten da sind, springt der
Spieler selbst; darüber hinaus wird der Strom neu gebaut. Weiss niemand,
wie weit es reicht, wird ebenfalls neu gebaut — ein neuer Strom dauert
ein paar Sekunden, ein Sprung, der nichts tut, ist kaputt.

Entschieden wird vor dem Aufräumen: `seek` verwirft den Vorrat, und
genau an dem hängt die Antwort.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-10 21:01:35 +02:00
co-authored by Claude Opus 5
parent 064f8f61af
commit 5657cd9e4b
+27 -9
View File
@@ -1549,6 +1549,10 @@ final class PlayerModel {
// Der Sprung **ist** angekommen, in dem Moment, in dem man ihn // Der Sprung **ist** angekommen, in dem Moment, in dem man ihn
// auslöst. Alles Weitere ist Warten auf Daten, und dafür gibt es den // auslöst. Alles Weitere ist Warten auf Daten, und dafür gibt es den
// Kreisel. // Kreisel.
// **Vor dem Aufräumen entscheiden.** Die Antwort hängt daran, wie
// weit der Vorrat reicht — und der wird gleich verworfen.
let neuerStrom = ausserhalbDesStroms(time)
currentTime = time currentTime = time
seekTarget = time seekTarget = time
// Der Vorrat von vorhin gilt für die alte Stelle. Ihn stehen zu lassen // Der Vorrat von vorhin gilt für die alte Stelle. Ihn stehen zu lassen
@@ -1566,7 +1570,7 @@ final class PlayerModel {
// der Spieler bliebe mit einem Kreisel stehen. Also wird der Strom // der Spieler bliebe mit einem Kreisel stehen. Also wird der Strom
// neu aufgebaut — `restart` steigt bei `currentTime` wieder ein, und // neu aufgebaut — `restart` steigt bei `currentTime` wieder ein, und
// das ist genau die Stelle, die eben gesetzt wurde. // das ist genau die Stelle, die eben gesetzt wurde.
if ausserhalbDesStroms(time) { if neuerStrom {
Task { await restart() } Task { await restart() }
return return
} }
@@ -1580,18 +1584,32 @@ final class PlayerModel {
/// neue Sitzung zu starten hiesse, Sekunden zu warten, um fünf Sekunden /// neue Sitzung zu starten hiesse, Sekunden zu warten, um fünf Sekunden
/// zurückzugehen. /// zurückzugehen.
private func ausserhalbDesStroms(_ time: Duration) -> Bool { private func ausserhalbDesStroms(_ time: Duration) -> Bool {
// Ein Strom ohne Vorlauf und ohne bekanntes Ende ist eine ganze // **Eine Datei mit bekannter Länge: überallhin.** `streamEnd` steht
// Datei: dort springt der Spieler überall hin. // nur, wenn der Player eine Dauer ermitteln konnte, und das kann er
if streamOffset == .zero, streamEnd == nil { return false } // genau dann, wenn das Ganze schon dasteht — eine Mediendatei oder
// eine fertige Abspielliste.
if streamEnd != nil, streamOffset == .zero { return false }
// Vor dem Anfang des Stroms gibt es in ihm nichts.
if time < streamOffset { return true } if time < streamOffset { return true }
// **Eine halbe Sekunde Nachsicht.** Das Ende der Liste und das Ende // **Eine wachsende Liste reicht so weit, wie Daten da sind.**
// des Erzeugten gehen um ein Stück auseinander; genau an der Kante //
// einen neuen Strom zu bauen wäre Aufwand für nichts. // Hier stand ein Vergleich mit `streamEnd`, und der ging nie auf:
if let ende = streamEnd, time > ende + .milliseconds(500) { return true } // bei einer wachsenden Liste bleibt die Dauer unbestimmt, also ist
// `streamEnd` nichts, und jeder Sprung galt als innerhalb. AVPlayer
// bekam ein Ziel, zu dem es keine Schnipsel gibt, verwarf es, und
// der Regler sprang zurück — beim Ziehen sichtbar, im Pausieren
// nicht, weil die Liste dort weiterwuchs und ihn irgendwann einholte.
//
// **Weiss niemand, wie weit es reicht, wird neu gebaut.** Ein neuer
// Strom dauert ein paar Sekunden; ein Sprung, der nichts tut, ist
// kaputt.
guard let bis = bufferedUntil else { return true }
return false // Eine halbe Sekunde Nachsicht an der Kante: dort einen neuen Strom
// zu bauen wäre Aufwand für nichts.
return time > bis + .milliseconds(500)
} }
/// Springt relativ zur aktuellen Stelle — für die ±10-Sekunden-Knöpfe. /// Springt relativ zur aktuellen Stelle — für die ±10-Sekunden-Knöpfe.