Live-TV: abwarten, bis der Empfänger abgestimmt hat

Die Antwort auf das Belegen sagt es deutlich:

    "status":"inprogress", "currentSize":0, "percent":-1.0

Der Tuner fängt in diesem Moment erst an. Ich habe im selben Atemzug nach
dem Strom gefragt — nach etwas, das es noch nicht gab. Dass darauf 400
und 404 kommen, ist kein Rätsel, sondern die richtige Antwort auf eine zu
frühe Frage.

Der Vorgang hat eine eigene Adresse, unter der ihm zuzusehen ist:
/media/grabbers/operations/… Dort wird jetzt gewartet, bis `currentSize`
über null steht, höchstens zwölf Sekunden. Länger hieße, jemanden vor
einem schwarzen Bild sitzen zu lassen.

Dazu steht die Sitzung selbst im Bericht — dorthin zeigt der Pfad, den
der Transcoder bekommt, und wie sie nach dem Abstimmen aussieht, habe ich
noch nie gesehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-12 15:50:20 +02:00
co-authored by Claude Opus 5
parent af60b02691
commit 2c66e61c71
@@ -702,6 +702,27 @@ extension PlexServer {
),
]
// Abwarten, bis der Empfänger abgestimmt hat. Er meldet
// `status: inprogress` und `currentSize: 0` — es gibt in dem Moment
// noch keinen Strom, und jede Frage danach musste scheitern. Ein
// Tuner ist ein Stück Hardware; er braucht Sekunden, keine
// Millisekunden.
if let operation = Self.grabOperationKey(in: data) {
probes.append(ChannelProbe(
path: "Abstimmen abwarten",
result: await waitForGrab(atKey: operation)
))
}
// Und wie die Sitzung danach aussieht — dorthin zeigt der Pfad, den
// der Transcoder bekommt.
if let sessionKey = Self.firstSessionKey(in: data) {
probes.append(ChannelProbe(
path: sessionKey,
result: await rawResponse(forPath: sessionKey, limit: 700)
))
}
// Der Weg des Plex-Clients: die **Sitzung**, die der Server gerade
// angelegt hat, wird umgerechnet — nicht die Abspielliste darin und
// nicht meine eigene Kennung. Ihr Schlüssel steht in der Antwort.
@@ -771,6 +792,64 @@ extension PlexServer {
return (outcome.url, subscription, probes)
}
/// Die Adresse, unter der dem Empfänger beim Abstimmen zuzusehen ist.
static func grabOperationKey(in data: Data) -> String? {
guard let root = try? JSONSerialization.jsonObject(with: data) else { return nil }
return firstKey(in: root, matching: "/media/grabbers/operations/")
}
private static func firstKey(in value: Any, matching prefix: String) -> String? {
if let dictionary = value as? [String: Any] {
if let key = dictionary["key"] as? String, key.hasPrefix(prefix) { return key }
for nested in dictionary.values {
if let found = firstKey(in: nested, matching: prefix) { return found }
}
}
if let array = value as? [Any] {
for nested in array {
if let found = firstKey(in: nested, matching: prefix) { return found }
}
}
return nil
}
/// Wartet, bis der Empfänger Daten liefert.
///
/// Höchstens zwölf Sekunden. Länger zu warten hieße, den Nutzer vor einem
/// schwarzen Bild sitzen zu lassen; gar nicht zu warten hieß bisher, nach
/// einem Strom zu fragen, den es noch nicht gibt.
private func waitForGrab(atKey key: String, limit: Int = 12) async -> String {
for second in 1...limit {
try? await Task.sleep(for: .seconds(1))
guard let data = try? await client.send(
PlexRequest(path: key, timeout: .seconds(10))
) else { continue }
guard let root = try? JSONSerialization.jsonObject(with: data),
let size = Self.firstNumber(in: root, forKey: "currentSize")
else { continue }
if size > 0 { return "nach \(second) s: \(size) Bytes" }
}
return "nach \(limit) s noch keine Daten"
}
private static func firstNumber(in value: Any, forKey name: String) -> Int? {
if let dictionary = value as? [String: Any] {
if let number = dictionary[name] as? NSNumber { return number.intValue }
for nested in dictionary.values {
if let found = firstNumber(in: nested, forKey: name) { return found }
}
}
if let array = value as? [Any] {
for nested in array {
if let found = firstNumber(in: nested, forKey: name) { return found }
}
}
return nil
}
/// Die Antwort des Entscheiders im Wortlaut.
private func rawDecision(percentEncodedItems items: [URLQueryItem]) async -> String {
guard let baseURL = connection.url,