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) + } +}