diff --git a/Packages/FlixyKit/Sources/FlixyProbe/Leiter.swift b/Packages/FlixyKit/Sources/FlixyProbe/Leiter.swift index 00c8f94..eb76bff 100644 --- a/Packages/FlixyKit/Sources/FlixyProbe/Leiter.swift +++ b/Packages/FlixyKit/Sources/FlixyProbe/Leiter.swift @@ -321,7 +321,10 @@ func jellyfinLeiter(host: String) async { ) print(mitABR) - if mitABR.contains("1 Stufe:") { erklärungWarumKeineLeiter() } + if mitABR.contains("1 Stufe:") { + erklärungWarumKeineLeiter() + await prüfeSelbstgebauteLeiter(master: master, kopfzeilen: kopf) + } // Aufräumen: die Aushandlung hat am Server eine Sitzung angelegt, und // ohne dieses Signal rechnet der Transcoder weiter, obwohl niemand @@ -354,3 +357,59 @@ func erklärungWarumKeineLeiter() { print(" Kam dieser Aufruf von zuhause, ist Punkt 1 die Erklärung — und dann") print(" sagt die Messung nichts darüber, was von unterwegs käme.") } + +/// Prüft, ob wir uns die Leiter **selbst** bauen könnten. +/// +/// Eine Master-Playlist ist nur ein Menü: eine Liste von Adressen mit Angaben +/// dazu. Jellyfins eigene Leiter besteht aus derselben `main.m3u8`-Adresse, +/// nur mit anderem `VideoBitrate`. Liefert der Server diese Unter-Playlists +/// auch dann, wenn **wir** die Zahl ändern, dann braucht es seine Zustimmung +/// gar nicht — dann schreiben wir das Menü. +/// +/// Zusätzlich mit `maxHeight`: Jellyfins eigene Leiter lässt die Auflösung +/// stehen und dreht nur an der Bitrate. Eine Stufe, die auch kleiner rechnet, +/// sieht bei wenig Bandbreite deutlich besser aus. +func prüfeSelbstgebauteLeiter(master: URL, kopfzeilen: [String: String]) async { + print("") + print("=== Ginge eine selbstgebaute Leiter? ===") + + guard let (daten, _) = try? await URLSession.shared.data(for: anfrage(master, kopfzeilen)), + let zeile = String(decoding: daten, as: UTF8.self) + .split(separator: "\n") + .first(where: { $0.hasPrefix("main.m3u8") }), + let unter = URL(string: String(zeile), relativeTo: master)?.absoluteURL + else { + print(" Keine Unter-Playlist im Menü gefunden.") + return + } + + let versuche: [(String, Int, Int?)] = [ + ("halbe Bitrate", 3_000_000, nil), + ("ein Viertel", 1_500_000, nil), + ("ein Viertel + 480p", 1_500_000, 480), + ] + + for (name, bitrate, höhe) in versuche { + var ziel = setzend("VideoBitrate", auf: String(bitrate), in: unter) + if let höhe { ziel = setzend("maxHeight", auf: String(höhe), in: ziel) } + + if let (roh, antwort) = try? await URLSession.shared.data(for: anfrage(ziel, kopfzeilen)) { + let status = (antwort as? HTTPURLResponse)?.statusCode ?? 0 + let text = String(decoding: roh, as: UTF8.self) + let schnipsel = text.split(separator: "\n").count { $0.hasPrefix("#EXTINF") } + print(" \(name): HTTP \(status), \(schnipsel) Schnipsel") + } else { + print(" \(name): nicht erreichbar") + } + } + + print("") + print(" Kommen hier überall Schnipsel zurück, brauchen wir Jellyfins") + print(" Zustimmung nicht — dann schreiben wir das Menü selbst.") +} + +private func anfrage(_ url: URL, _ kopfzeilen: [String: String]) -> URLRequest { + var request = URLRequest(url: url) + for (name, wert) in kopfzeilen { request.setValue(wert, forHTTPHeaderField: name) } + return request +}