Live-TV: den Medien-Block ansehen, statt ihn zu zählen
„1 Fassung, keine Teil-Adresse" beendet nichts. Wenn dort kein abspielbarer Pfad steht, zählt, was stattdessen dort steht — irgendwo muss der Server sagen, wie diese Sendung abzuspielen ist. Der Bericht zeigt den Medien-Block jetzt Feld für Feld. Dazu zwei Dinge, die ich benutzt habe, ohne sie je gesehen zu haben: der Raster-Schlüssel des Senders und seine übrigen Kennungen stehen jetzt im Bericht. Und der Raster-Schlüssel wird als vollständige Adresse probiert — bisher war immer nur sein letzter Abschnitt als Sitzungsschlüssel an der Reihe. Der Empfänger kommt ebenfalls dazu: /livetv/dvrs/2/channels antwortet auf diesem Server gar nicht, aber /media/grabbers/devices/1/channels führt 278 Sender. Wenn der DVR keine kennt, kennt sie das Gerät. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
62a913f2cd
commit
162247e8dc
@@ -380,9 +380,40 @@ extension PlexServer {
|
|||||||
maximumBitrate: Int?
|
maximumBitrate: Int?
|
||||||
) async -> LiveTuneResult {
|
) async -> LiveTuneResult {
|
||||||
let session = UUID().uuidString
|
let session = UUID().uuidString
|
||||||
var trace: [ChannelProbe] = []
|
|
||||||
var attempts = 0
|
var attempts = 0
|
||||||
|
|
||||||
|
// Womit überhaupt gearbeitet wird. Ich habe den Raster-Schlüssel
|
||||||
|
// benutzt, ohne ihn je gesehen zu haben — und im Suchraum nur seinen
|
||||||
|
// letzten Abschnitt verwendet.
|
||||||
|
var trace: [ChannelProbe] = [ChannelProbe(
|
||||||
|
path: "Sender-Schlüssel",
|
||||||
|
result: [
|
||||||
|
"gridKey=\(channel.gridKey ?? "–")",
|
||||||
|
"key=\(channel.key ?? "–")",
|
||||||
|
"id=\(channel.id)",
|
||||||
|
"nummer=\(channel.channelIdentifier ?? "–")",
|
||||||
|
].joined(separator: ", ")
|
||||||
|
)]
|
||||||
|
|
||||||
|
// Der Raster-Schlüssel als Adresse, unverkürzt. Bisher habe ich nur
|
||||||
|
// seinen letzten Abschnitt als Sitzungsschlüssel probiert — als
|
||||||
|
// vollständige Adresse war er nie an der Reihe.
|
||||||
|
if let gridKey = channel.gridKey, gridKey.hasPrefix("/") {
|
||||||
|
attempts += 1
|
||||||
|
let outcome = await attemptStream(
|
||||||
|
path: gridKey,
|
||||||
|
ratingKey: channel.id,
|
||||||
|
mediaIndex: 0,
|
||||||
|
includeProfile: true,
|
||||||
|
maximumBitrate: maximumBitrate,
|
||||||
|
session: session
|
||||||
|
)
|
||||||
|
trace.append(outcome.probe)
|
||||||
|
if let url = outcome.url {
|
||||||
|
return LiveTuneResult(url: url, sessionIdentifier: session, trace: trace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Die Sendung erst nachschlagen. Das Raster nennt keine abspielbare
|
// Die Sendung erst nachschlagen. Das Raster nennt keine abspielbare
|
||||||
// Adresse — die Einzelabfrage beim Programmanbieter womöglich schon.
|
// Adresse — die Einzelabfrage beim Programmanbieter womöglich schon.
|
||||||
// Ihre Adresse blind an den Transcoder zu geben, hat dreimal eine
|
// Ihre Adresse blind an den Transcoder zu geben, hat dreimal eine
|
||||||
@@ -526,11 +557,42 @@ extension PlexServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let parts = item.media.flatMap(\.parts).compactMap(\.key)
|
let parts = item.media.flatMap(\.parts).compactMap(\.key)
|
||||||
let summary = parts.isEmpty
|
guard parts.isEmpty else {
|
||||||
? "\(item.media.count) Fassungen, keine Teil-Adresse — \(Self.containerSummary(data))"
|
return (parts, item.ratingKey, "\(parts.count) Teil-Adressen")
|
||||||
: "\(parts.count) Teil-Adressen"
|
}
|
||||||
|
|
||||||
return (parts, item.ratingKey, summary)
|
// Keine Teil-Adresse — dann zählt, was stattdessen dort steht.
|
||||||
|
// „1 Fassung, keine Teil-Adresse" beendet nichts; der Aufbau der
|
||||||
|
// Antwort schon, denn irgendwo muss der Server sagen, wie diese
|
||||||
|
// Sendung abzuspielen ist.
|
||||||
|
return ([], item.ratingKey, "ohne Teil-Adresse\n" + Self.mediaOutline(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Der Aufbau des Medien-Blocks einer Antwort, kurz gehalten.
|
||||||
|
static func mediaOutline(_ data: Data) -> String {
|
||||||
|
guard let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
||||||
|
let container = object["MediaContainer"] as? [String: Any],
|
||||||
|
let first = (container["Metadata"] as? [[String: Any]])?.first
|
||||||
|
else { return Self.containerSummary(data) }
|
||||||
|
|
||||||
|
var lines: [String] = []
|
||||||
|
if let media = (first["Media"] as? [[String: Any]])?.first {
|
||||||
|
lines += media.keys.sorted().map { key in
|
||||||
|
let value = media[key]!
|
||||||
|
if let text = value as? String { return " Media.\(key) = \(text)" }
|
||||||
|
if let number = value as? NSNumber { return " Media.\(key) = \(number)" }
|
||||||
|
if let array = value as? [Any] { return " Media.\(key): \(array.count) Einträge" }
|
||||||
|
return " Media.\(key): \(type(of: value))"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lines.append(" kein Media-Block")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Und die Kurzform der Sendung selbst — dort stehen `key` und `guid`.
|
||||||
|
for key in ["key", "guid", "ratingKey", "type", "live", "onAir"] where first[key] != nil {
|
||||||
|
lines.append(" \(key) = \(first[key]!)")
|
||||||
|
}
|
||||||
|
return lines.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Baut eine Stromadresse und ruft sie einmal ab.
|
/// Baut eine Stromadresse und ruft sie einmal ab.
|
||||||
@@ -628,12 +690,26 @@ extension PlexServer {
|
|||||||
var paths: [String] = []
|
var paths: [String] = []
|
||||||
|
|
||||||
if let dvr {
|
if let dvr {
|
||||||
for key in [channel.gridKey, channel.key, channel.channelIdentifier, channel.id]
|
let keys = [channel.gridKey, channel.key, channel.channelIdentifier, channel.id]
|
||||||
.compactMap({ $0 })
|
.compactMap { $0 }
|
||||||
.map({ $0.split(separator: "/").last.map(String.init) ?? $0 }) {
|
.map { $0.split(separator: "/").last.map(String.init) ?? $0 }
|
||||||
|
|
||||||
|
for key in keys {
|
||||||
paths.append("/livetv/dvrs/\(dvr.key)/channels/\(key)/tune")
|
paths.append("/livetv/dvrs/\(dvr.key)/channels/\(key)/tune")
|
||||||
paths.append("/livetv/dvrs/\(dvr.key)/channels/\(key)")
|
paths.append("/livetv/dvrs/\(dvr.key)/channels/\(key)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Und über das Gerät. Es ist die einzige Stelle, die auf diesem
|
||||||
|
// Server überhaupt Sender führt — 278 Stück unter `DeviceChannel`.
|
||||||
|
// Wenn der DVR keine kennt, kennt sie das Gerät.
|
||||||
|
for device in dvr.devices {
|
||||||
|
guard let deviceKey = device.key else { continue }
|
||||||
|
let base = deviceKey.hasPrefix("/") ? deviceKey : "/media/grabbers/devices/\(deviceKey)"
|
||||||
|
for key in keys.prefix(2) {
|
||||||
|
paths.append("\(base)/channels/\(key)/tune")
|
||||||
|
paths.append("\(base)/channels/\(key)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paths += sessionKeys(channel: channel, program: program).map { "\(sessionPath)/\($0)" }
|
paths += sessionKeys(channel: channel, program: program).map { "\(sessionPath)/\($0)" }
|
||||||
|
|||||||
Reference in New Issue
Block a user