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:
co-authored by
Claude Opus 5
parent
008f6b7fd7
commit
5e1a878237
@@ -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
|
// Abwarten, bis der Empfänger abgestimmt hat. Er meldet
|
||||||
// `status: inprogress` und `currentSize: 0` — es gibt in dem Moment
|
// `status: inprogress` und `currentSize: 0` — es gibt in dem Moment
|
||||||
// noch keinen Strom, und jede Frage danach musste scheitern. Ein
|
// 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" })
|
#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")
|
@Test("Die Teil-Adresse wird gefunden, egal wie tief sie liegt")
|
||||||
func findsPartAtAnyDepth() throws {
|
func findsPartAtAnyDepth() throws {
|
||||||
// Der Vorgang liegt je nach Serverfassung unterschiedlich tief
|
// Der Vorgang liegt je nach Serverfassung unterschiedlich tief
|
||||||
|
|||||||
Reference in New Issue
Block a user