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:
co-authored by
Claude Opus 5
parent
03a8d1f899
commit
7987dbf9a4
@@ -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»")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user