Untertitel brachten den Vorratsspeicher auf 404

Untertitel stehen in der Abspielliste als eigene Spur unter EXT-X-MEDIA —
eine Anweisung mit einer Adresse darin. Umgeschrieben habe ich bisher nur
EXT-X-MAP, also blieb diese relativ stehen und zeigte danach auf den
lokalen Server, wo es sie nicht gibt. Zu sehen war der Fehler erst, als
jemand Untertitel einschaltete: HTTP 404, mitten im Film.

Jetzt sagt der Umschreiber, **was** fuer eine Adresse er gerade in der Hand
hat: eine Qualitaetsstufe, eine Nebenspur, ein Kopfstueck oder ein
Schnipsel. Das ist noetig, weil sie Verschiedenes wollen — hinter einer
Stufe liegt Bild und die soll vorgeladen werden, hinter einer Untertitel-
spur nicht. Sonst lueden wir Untertitel statt Bild: der Film bliebe stehen,
waehrend die Anzeige reichlich Vorrat meldet.

Dazu wandern alle Adressen im lokalen Server auf die **vollstaendige**
Form. Eine relative gilt je nach Liste gegen eine andere Grundadresse — die
Untertitelspur liegt woanders als das Bild —, und wer das erst beim Abholen
aufloest, loest es irgendwann gegen die falsche auf. Das waere der naechste
Fehler derselben Art gewesen.

Drei Tests halten die Unterscheidung fest, darunter die Liste mit
Untertitelspur, die den 404 ausgeloest hat.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-28 21:36:35 +02:00
co-authored by Claude Opus 5
parent 88ba1a540c
commit cff0c06745
4 changed files with 180 additions and 83 deletions
@@ -62,52 +62,71 @@ public struct MediaPlaylist: Sendable, Equatable {
self.isMaster = master
}
/// Was für eine Adresse an einer Stelle steht.
///
/// **Warum das jemanden interessiert.** Eine Abspielliste enthält
/// viererlei Adressen, und sie wollen Verschiedenes: eine Qualitätsstufe
/// führt zu weiteren Schnipseln und soll vorgeladen werden, eine
/// Untertitelspur führt zu einer eigenen Liste und soll es **nicht** —
/// sonst lädt der Vorrat Untertitel statt Bild.
public enum Reference: Sendable, Equatable {
/// Eine Qualitätsstufe — dahinter liegt das Bild.
case level
/// Eine Nebenspur: Untertitel oder zusätzlicher Ton.
case rendition
/// Das Kopfstück einer fragmentierten MP4.
case map
/// Ein Schnipsel.
case segment
}
/// Schreibt jede Adresse um, ohne sonst etwas anzufassen.
///
/// Die Reihenfolge bleibt, die Anweisungen bleiben, die Leerzeilen
/// bleiben. Nur wo eine Adresse stand, steht danach eine andere.
public func rewritingReferences(_ transform: (String) -> String) -> String {
///
/// **Auch die Adressen in Anweisungen.** Zwei Anweisungen tragen eine:
/// `EXT-X-MAP` das Kopfstück und `EXT-X-MEDIA` die Nebenspuren. Beide
/// stehen zu lassen ergibt eine Liste, die halb auf uns und halb ins
/// Leere zeigt — und das endet mit „404", sobald jemand Untertitel
/// einschaltet.
public func rewritingReferences(_ transform: (String, Reference) -> String) -> String {
var ergebnis: [String] = []
ergebnis.reserveCapacity(lines.count)
var nächsteIstStufe = false
for zeile in lines {
let getrimmt = zeile.trimmingCharacters(in: .whitespaces)
if getrimmt.isEmpty {
if getrimmt.hasPrefix("#EXT-X-STREAM-INF") {
nächsteIstStufe = true
ergebnis.append(zeile)
} else if getrimmt.hasPrefix("#EXT-X-MEDIA:") {
ergebnis.append(Self.rewritingURI(in: zeile) { transform($0, .rendition) })
} else if getrimmt.hasPrefix("#EXT-X-MAP:") {
// **Die eine Anweisung, in der eine Adresse steckt.**
//
// Sie zeigt auf das Kopfstück einer fragmentierten MP4 — ohne
// das ist kein einziger Schnipsel zu dekodieren. Bliebe sie
// stehen, zeigte sie relativ auf den lokalen Server, und dort
// gibt es sie nicht: schwarzes Bild, ohne dass an den
// Schnipseln irgendetwas falsch wäre.
ergebnis.append(Self.rewritingMapURI(zeile, transform))
} else if getrimmt.hasPrefix("#") {
ergebnis.append(Self.rewritingURI(in: zeile) { transform($0, .map) })
} else if getrimmt.isEmpty || getrimmt.hasPrefix("#") {
ergebnis.append(zeile)
} else {
ergebnis.append(transform(getrimmt))
ergebnis.append(transform(getrimmt, nächsteIstStufe ? .level : .segment))
nächsteIstStufe = false
}
}
return ergebnis.joined(separator: "\n")
}
/// Tauscht die Adresse in `#EXT-X-MAP:URI="…"` aus, sonst nichts.
private static func rewritingMapURI(
_ zeile: String,
/// Tauscht die Adresse in `URI="…"` aus, sonst nichts.
private static func rewritingURI(
in zeile: String,
_ transform: (String) -> String
) -> String {
let marke = "URI=\""
guard let start = zeile.range(of: marke),
let ende = zeile[start.upperBound...].firstIndex(of: "\"")
else { return zeile }
let ende2 = ende..<zeile.index(after: ende)
let adresse = String(zeile[start.upperBound..<ende2.lowerBound])
return zeile.replacingCharacters(
in: start.upperBound..<ende2.lowerBound,
with: transform(adresse)
)
let adresse = String(zeile[start.upperBound..<ende])
return zeile.replacingCharacters(in: start.upperBound..<ende, with: transform(adresse))
}
/// Ob die Liste vollständig ist — dann kommen keine Schnipsel mehr dazu.