Live-TV: die Sender waren da, zwei eigene Fehler haben sie verschluckt

Der Abzug vom Server zeigt beides schwarz auf weiß.

Erstens: /media/grabbers/devices/1/channels antwortet mit 278 Sendern —
unter dem Sammelnamen `DeviceChannel`. Die Hülle kannte nur `Channel` und
hat die vollständige Liste als leer verworfen.

Zweitens: das Programmraster antwortet, aber nicht auf `type=1`. Die 1
steht bei Plex für Film, und im Programm laufen auch Folgen, Sport und
Nachrichten — der Filter hat das Raster geleert. Ohne ihn kommen die
Sendungen samt Sendername, Nummer, Logo und Kennung.

Das Raster hat jetzt Vorrang vor der Tunerliste, auch wenn beide
antworten: nur dort gehören Sender und Programm zusammen, es enthält
ohnehin nur die eingeschalteten Sender, und die Tunerliste führt alle 278.

Einschalten läuft über die Adresse der laufenden Sendung beim
Programmanbieter — Plex behandelt eine laufende Sendung wie einen Film.
Die Adresse wird einmal entpackt, bevor sie in die Anfrage geht: sonst
kodiert URLComponents ein zweites Mal, und aus %3A wird %253A.

Die Tests stehen jetzt auf den echten Antworten aus dem Abzug, nicht auf
erfundenen: DeviceChannel mit `name` und `key`, Rastereinträge mit
channelVcn, channelID und einer Folge neben einem Film.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-12 13:08:15 +02:00
co-authored by Claude Opus 5
parent 7e36f37506
commit 2362865ba9
9 changed files with 2726 additions and 2578 deletions
+10 -3
View File
@@ -71,7 +71,7 @@ struct LiveTVView: View {
channels: channels,
programs: programs,
tuning: isTuning,
onTune: { channel in Task { await tune(channel) } }
onTune: { channel, program in Task { await tune(channel, program: program) } }
)
} else {
channelList
@@ -180,13 +180,20 @@ struct LiveTVView: View {
trace = guide.trace
}
private func tune(_ channel: PlexChannel) async {
private func tune(_ channel: PlexChannel, program: PlexProgram? = nil) async {
guard let server = session.server, isTuning == nil else { return }
isTuning = channel.id
defer { isTuning = nil }
// Die laufende Sendung mitgeben: über ihre Adresse beim
// Programmanbieter führt der direkte Weg zum Strom.
let running = program ?? programs[channel.id]?.first { $0.isRunning() }
let policy = settings.policy(for: network)
guard (try? await server.tune(channel: channel, maximumBitrate: policy.maximumBitrate)) != nil
guard (try? await server.tune(
channel: channel,
program: running,
maximumBitrate: policy.maximumBitrate
)) != nil
else { return }
// Live-TV läuft über denselben Player wie alles andere. Der Schlüssel
+8 -3
View File
@@ -18,7 +18,7 @@ struct ProgramGuide: View {
let channels: [PlexChannel]
let programs: [String: [PlexProgram]]
let tuning: String?
let onTune: (PlexChannel) -> Void
let onTune: (PlexChannel, PlexProgram?) -> Void
@Environment(FlixySession.self) private var session
@Environment(\.flixyAccent) private var accent
@@ -53,7 +53,7 @@ struct ProgramGuide: View {
channel: selection.channel,
program: selection.program,
isRunning: selection.program.isRunning(at: now),
onTune: { onTune(selection.channel) }
onTune: { onTune(selection.channel, selection.program) }
)
}
// Die Fortschrittsbalken und die Jetzt-Linie stimmen sonst nach ein
@@ -105,10 +105,15 @@ struct ProgramGuide: View {
.background(Color.flixyCanvas)
}
/// Was auf diesem Sender gerade läuft.
private func running(on channel: PlexChannel) -> PlexProgram? {
programs[channel.id]?.first { $0.isRunning(at: now) }
}
private var channelColumn: some View {
VStack(spacing: 0) {
ForEach(channels) { channel in
Button { onTune(channel) } label: {
Button { onTune(channel, running(on: channel)) } label: {
HStack(spacing: FlixySpacing.snug) {
FlixyArtwork(
url: session.server?.artworkURL(path: channel.thumb, width: 120, height: 120),