Probe: den Entscheider fragen, wenn der Transcoder schweigt

Der Transcoder antwortet auf jede Form der Adresse mit einer nackten
„400 Bad Request"-Seite — ohne Plex-Meldung, ohne Hinweis. Der Entscheider
bekommt dieselben Angaben und gibt eine Plex-Antwort zurück. Das ist die
letzte Stelle, an der dieser Server noch erklärt, woran es liegt.

Schlägt das Einschalten fehl, zeigt der Probe deshalb jetzt zusätzlich
die Antwort des Entscheiders — in beiden Kodierformen — sowie den
Empfänger, das Gerät und /livetv/sessions im Wortlaut.

Dazu kann der Probe beliebige Adressen abfragen:
`swift run FlixyProbe /livetv/dvrs/2`. Eine Schnittstelle ohne
Beschreibung erkundet man, indem man sie fragt.

Der Token wird aus jeder Ausgabe entfernt, falls der Server ihn
zurückspiegelt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-12 15:12:51 +02:00
co-authored by Claude Opus 5
parent 03a8d1f899
commit 7987dbf9a4
2 changed files with 98 additions and 0 deletions
@@ -1042,3 +1042,72 @@ public enum LiveTVVerdict: Sendable, Equatable {
/// Der Entscheider hat nicht geantwortet — kein Grund, es nicht zu versuchen.
case unknown(String)
}
extension PlexServer {
/// Was der Entscheider zu einer Sendung sagt — im Wortlaut.
///
/// Der Transcoder antwortet mit einer nackten „400 Bad Request"-Seite und
/// verrät nichts. Der Entscheider bekommt dieselben Angaben, gibt aber
/// eine Plex-Antwort zurück, in der steht, woran es liegt. Das ist die
/// letzte Stelle, an der dieser Server noch etwas erklärt.
public func decisionDump(channel: PlexChannel, program: PlexProgram?) async -> String {
guard let metadataKey = program?.metadataKey else {
return "Keine Sendung — ohne sie gibt es nichts zu entscheiden."
}
var report: [String] = []
for keepEncoding in [true, false] {
let request = TranscodeRequest(
ratingKey: channel.id,
isLocal: connection.local,
pathOverride: metadataKey
)
guard let url = transcodeURL(
for: request,
keepPathEncoding: keepEncoding
) else { continue }
guard let items = URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems
else { continue }
report.append("--- Entscheider, \(keepEncoding ? "einfach" : "doppelt") kodiert ---")
do {
let data = try await client.send(PlexRequest(
path: "/video/:/transcode/universal/decision",
query: items,
timeout: .seconds(20)
))
report.append(redacted(String(decoding: data.prefix(900), as: UTF8.self)))
} catch {
report.append(Self.shortDescription(of: error))
}
}
return report.joined(separator: "\n")
}
/// Die rohe Antwort auf eine Adresse, gekürzt und ohne Token.
///
/// Zum Erkunden gedacht: die Live-Schnittstelle von Plex ist nirgends
/// beschrieben, und die Antwort auf „was steht unter dieser Adresse" ist
/// die einzige verlässliche Quelle. Der Token wird aus der Ausgabe
/// entfernt, falls der Server ihn irgendwo zurückspiegelt.
public func rawResponse(forPath path: String, limit: Int = 1_200) async -> String {
let encoded = path.contains("%")
do {
let data = try await client.send(PlexRequest(
path: path,
timeout: .seconds(20),
isPathEncoded: encoded
))
let text = String(decoding: data.prefix(limit), as: UTF8.self)
return redacted(text)
} catch {
return Self.shortDescription(of: error)
}
}
private nonisolated func redacted(_ text: String) -> String {
text.replacingOccurrences(of: token, with: "«token»")
}
}