Live-TV: es waren zwei verschiedene UUIDs

Die vollständige Sitzung zeigt es nebeneinander:

    Tune-Antwort:   …/sessions/df16c509…/B5080949-E767-…/index.m3u8
    Sitzung selbst: …/sessions/df16c509…/2F3939E2-6196-…/index.m3u8

Dieselbe Adresse, aber mit verschiedenen Kennungen darin: die
Tune-Antwort setzt die Sitzungskennung ein, die Sitzung selbst die
Gerätekennung. Nur die zweite gibt es wirklich. Ich habe die erste
genommen — daher jedes Mal 404.

Gesehen habe ich das erst, als die Antwort nicht mehr bei 700 Zeichen
abgeschnitten war. Genau dort fing `Part` an.

Dieselbe Antwort trägt auch `TranscodeSession` — der Strom läuft bereits.
Es gab nie etwas umzurechnen, und der Transcoder hatte recht, jede dieser
Anfragen abzulehnen.

Die Abspieladresse kommt jetzt ausschließlich aus der Sitzung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-12 16:10:27 +02:00
co-authored by Claude Opus 5
parent 008f6b7fd7
commit 5e1a878237
2 changed files with 48 additions and 0 deletions
@@ -702,6 +702,22 @@ extension PlexServer {
),
]
// Die Sitzung selbst fragen — und **nur** ihr die Abspieladresse
// glauben. Die Antwort auf das Belegen nennt eine Adresse mit der
// Sitzungskennung darin; die Sitzung nennt dieselbe Adresse mit der
// **Gerätekennung**. Nur die zweite gibt es wirklich. Ich habe die
// erste genommen und mich über 404 gewundert.
if let sessionKey = Self.firstSessionKey(in: data),
let sessionData = try? await client.send(
PlexRequest(path: sessionKey, timeout: .seconds(20))
),
let livePart = Self.firstPartKey(in: sessionData),
let direct = directStreamURL(forPath: livePart) {
let outcome = await probeStream(url: direct)
probes.append(ChannelProbe(path: "\(livePart) (aus der Sitzung)", result: outcome))
if outcome.hasPrefix("HTTP 200") { return (direct, subscription, probes) }
}
// 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
@@ -359,6 +359,38 @@ struct LiveTVGuideTests {
#expect(items.contains { $0.name == "hasMDE" && $0.value == "1" })
}
@Test("Die Abspieladresse kommt aus der Sitzung, nicht aus der Tune-Antwort")
func prefersPartFromSession() async throws {
// Beide Antworten nennen eine Adresse derselben Form, aber mit
// verschiedenen Kennungen darin: die Tune-Antwort setzt die
// Sitzungskennung ein, die Sitzung selbst die Gerätekennung. Nur die
// zweite gibt es wirklich — die erste ergab jedes Mal 404.
let tuned = """
{"MediaContainer":{"size":1,"MediaSubscription":[{"key":"22",
"MetadataItem":{"key":"/livetv/sessions/abc","live":"1",
"Media":[{"id":1,"Part":[{"key":"/livetv/sessions/abc/SITZUNGSKENNUNG/index.m3u8"}]}]}}]}}
"""
let session = """
{"MediaContainer":{"size":1,"Metadata":[{"key":"/livetv/sessions/abc","live":"1",
"Media":[{"id":1,"Part":[{"key":"/livetv/sessions/abc/GERAETEKENNUNG/index.m3u8?offset=-1.000000"}]}]}]}}
"""
let server = server(routes: [
"/channels/kanal-7/tune": tuned,
"/livetv/sessions/abc": session,
// Nur unter der Gerätekennung liefert der Server eine
// Abspielliste; die andere Form gibt es nicht.
"/GERAETEKENNUNG/index.m3u8": "#EXTM3U",
])
let channel = PlexChannel(id: "kanal-7", title: "SRF 1 HD", key: "2")
let result = await server.tune(channel: channel, dvr: PlexDVR(key: "2"), maximumBitrate: nil)
let url = try #require(result.url)
#expect(url.path() == "/livetv/sessions/abc/GERAETEKENNUNG/index.m3u8")
#expect(!url.absoluteString.contains("SITZUNGSKENNUNG"))
#expect(!url.absoluteString.contains("/transcode/"))
}
@Test("Die Teil-Adresse wird gefunden, egal wie tief sie liegt")
func findsPartAtAnyDepth() throws {
// Der Vorgang liegt je nach Serverfassung unterschiedlich tief