From 8ab7baecb880db221ccadc5501eb97578557cd49 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sat, 12 Sep 2026 15:56:25 +0200 Subject: [PATCH] Transcoder: es lag nie an Live-TV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Gegenprobe ist eindeutig: Gegenprobe — Transcoder mit: Mogli: Legende des Dschungels → HTTP 400 Eine gewöhnliche Datei scheitert genauso. Damit ist die Suche der letzten Tage an der falschen Stelle gelaufen: Transcoding hat in Flixy noch nie funktioniert, es ist nur nie aufgefallen. Filme laufen über DirectPlay, da wird der Transcoder gar nicht gefragt — Live-TV ist das Erste, was ihn zwingend braucht. Diese Annahme habe ich tagelang mitgeschleppt, ohne sie zu prüfen. Sie zu prüfen hat zehn Zeilen gekostet. Jetzt wird richtig halbiert, an einer Datei statt an einem Sender: von „nur Pfad und Token" aufwärts, Angabe für Angabe, bis eine davon die 400 auslöst. Dazu derselbe Satz an den Entscheider, der in Plex' Format antwortet statt mit HTML. Co-Authored-By: Claude Opus 5 --- .../FlixyKit/Sources/FlixyProbe/main.swift | 5 +- .../PlexKit/Endpoints/PlexServer+LiveTV.swift | 86 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/Packages/FlixyKit/Sources/FlixyProbe/main.swift b/Packages/FlixyKit/Sources/FlixyProbe/main.swift index f10285c..7154d91 100644 --- a/Packages/FlixyKit/Sources/FlixyProbe/main.swift +++ b/Packages/FlixyKit/Sources/FlixyProbe/main.swift @@ -93,7 +93,10 @@ if let hubs = try? await server.homeHubs(), ) if let url = server.transcodeURL(for: request) { print("Gegenprobe — Transcoder mit: \(movie.title)") - print(" → \(await server.probeStream(url: url))") + print(" volle Anfrage: \(await server.probeStream(url: url))") + for line in await server.transcoderBisect(ratingKey: movie.ratingKey) { + print(line) + } print() } } else { diff --git a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift index 81e0dc7..4af9072 100644 --- a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift +++ b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift @@ -1492,3 +1492,89 @@ extension CharacterSet { charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()" ) } + + +extension PlexServer { + /// Klopft den Transcoder mit verschiedenen Angaben ab. + /// + /// Eine gewöhnliche Datei scheitert genauso wie Live-TV: der Transcoder + /// antwortet auf alles mit einer nackten 400. Damit ist es kein + /// Live-Problem, sondern eines der Anfrage selbst — und welche Angabe + /// stört, sagt nur das Halbieren. + public func transcoderBisect(ratingKey: String) async -> [String] { + let session = UUID().uuidString + let path = "/library/metadata/\(ratingKey)" + let encoded = path.addingPercentEncoding(withAllowedCharacters: .plexParameterAllowed) ?? path + + let variants: [(String, [URLQueryItem])] = [ + ("nur Pfad und Token", [ + URLQueryItem(name: "path", value: encoded), + URLQueryItem(name: "X-Plex-Token", value: token), + ]), + ("Pfad unkodiert", [ + URLQueryItem(name: "path", value: path), + URLQueryItem(name: "X-Plex-Token", value: token), + ]), + ("mit Protokoll und Indizes", [ + URLQueryItem(name: "path", value: encoded), + URLQueryItem(name: "protocol", value: "hls"), + URLQueryItem(name: "mediaIndex", value: "0"), + URLQueryItem(name: "partIndex", value: "0"), + URLQueryItem(name: "X-Plex-Token", value: token), + ]), + ("dazu die Gerätekennung", [ + URLQueryItem(name: "path", value: encoded), + URLQueryItem(name: "protocol", value: "hls"), + URLQueryItem(name: "mediaIndex", value: "0"), + URLQueryItem(name: "partIndex", value: "0"), + URLQueryItem(name: "X-Plex-Token", value: token), + URLQueryItem(name: "X-Plex-Client-Identifier", value: identity.clientIdentifier), + ]), + ("dazu hasMDE", [ + URLQueryItem(name: "path", value: encoded), + URLQueryItem(name: "protocol", value: "hls"), + URLQueryItem(name: "hasMDE", value: "1"), + URLQueryItem(name: "mediaIndex", value: "0"), + URLQueryItem(name: "partIndex", value: "0"), + URLQueryItem(name: "X-Plex-Token", value: token), + URLQueryItem(name: "X-Plex-Client-Identifier", value: identity.clientIdentifier), + ]), + ("dazu die Sitzung", [ + URLQueryItem(name: "path", value: encoded), + URLQueryItem(name: "protocol", value: "hls"), + URLQueryItem(name: "hasMDE", value: "1"), + URLQueryItem(name: "mediaIndex", value: "0"), + URLQueryItem(name: "partIndex", value: "0"), + URLQueryItem(name: "session", value: session), + URLQueryItem(name: "X-Plex-Session-Identifier", value: session), + URLQueryItem(name: "X-Plex-Token", value: token), + URLQueryItem(name: "X-Plex-Client-Identifier", value: identity.clientIdentifier), + ]), + ] + + var lines: [String] = [] + for (name, items) in variants { + lines.append(" \(name): \(await probe(items: items, endpoint: "start.m3u8"))") + } + // Und derselbe Satz an den Entscheider: er antwortet in Plex' Format + // statt mit HTML, wenn er überhaupt antwortet. + lines.append(" Entscheider: \(await probe(items: variants.last!.1, endpoint: "decision"))") + return lines + } + + private func probe(items: [URLQueryItem], endpoint: String) async -> String { + guard let baseURL = connection.url, + var components = URLComponents( + url: baseURL.appending(path: "/video/:/transcode/universal/\(endpoint)"), + resolvingAgainstBaseURL: false + ) + else { return "Adresse nicht baubar" } + + components.percentEncodedQuery = items + .map { "\($0.name)=\($0.value ?? "")" } + .joined(separator: "&") + guard let url = components.url else { return "Adresse nicht baubar" } + + return await probeStream(url: url) + } +}