diff --git a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift index b31bbcc..4a6b608 100644 --- a/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift +++ b/Packages/FlixyKit/Sources/PlexKit/Endpoints/PlexServer+LiveTV.swift @@ -690,7 +690,17 @@ extension PlexServer { )]) } - var probes = [ChannelProbe(path: "POST \(path)", result: "Tuner belegt: \(part)")] + // Die ganze Antwort, nicht nur das, wonach ich gesucht habe. Ich + // habe sie mehrfach durchsucht und dabei übersehen, was sonst noch + // darin steht — Behälter, Teil-Kennungen, vielleicht eine fertige + // Transcode-Sitzung. + var probes = [ + ChannelProbe(path: "POST \(path)", result: "Tuner belegt: \(part)"), + ChannelProbe( + path: "Antwort auf das Belegen", + result: redacted(String(decoding: data.prefix(900), as: UTF8.self)) + ), + ] // Der Weg des Plex-Clients: die **Sitzung**, die der Server gerade // angelegt hat, wird umgerechnet — nicht die Abspielliste darin und @@ -713,6 +723,24 @@ extension PlexServer { probes.append(outcome.probe) if let url = outcome.url { return (url, subscription, probes) } } + + // Dieselben Angaben an den Entscheider. Der Transcoder antwortet + // mit HTML und schweigt sich aus; der Entscheider antwortet in + // Plex' eigenem Format und nennt den Grund. + let request = TranscodeRequest( + ratingKey: channel.id, + maximumBitrate: maximumBitrate, + isLocal: connection.local, + sessionIdentifier: session, + pathOverride: sessionKey + ) + if let url = transcodeURL(for: request), + let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.percentEncodedQueryItems { + probes.append(ChannelProbe( + path: "Entscheider zur Sitzung", + result: await rawDecision(percentEncodedItems: items) + )) + } } // Und die Abspielliste unmittelbar. Sie antwortete mit 404 statt 400: @@ -743,6 +771,33 @@ extension PlexServer { return (outcome.url, subscription, probes) } + /// Die Antwort des Entscheiders im Wortlaut. + private func rawDecision(percentEncodedItems items: [URLQueryItem]) async -> String { + guard let baseURL = connection.url, + var components = URLComponents( + url: baseURL.appending(path: "/video/:/transcode/universal/decision"), + resolvingAgainstBaseURL: false + ) + else { return "Adresse nicht baubar" } + components.percentEncodedQueryItems = items + + guard let url = components.url else { return "Adresse nicht baubar" } + var request = URLRequest(url: url) + request.timeoutInterval = 20 + for (field, value) in identity.headers { request.setValue(value, forHTTPHeaderField: field) } + request.setValue(token, forHTTPHeaderField: "X-Plex-Token") + + do { + let (data, response) = try await transport.data(for: request) + let status = (response as? HTTPURLResponse)?.statusCode ?? 0 + let body = String(decoding: data.prefix(700), as: UTF8.self) + .trimmingCharacters(in: .whitespacesAndNewlines) + return "HTTP \(status): \(redacted(body))" + } catch { + return Self.shortDescription(of: error) + } + } + /// Der Schlüssel der Live-Sitzung, die der Server angelegt hat. /// /// `/livetv/sessions/1268be73-…` — die Sitzung des **Servers**, nicht die