Live-TV: die Sender stehen im Programmraster, nicht in einer Senderliste
Drei geratene Adressen für die Senderliste antworteten auf einem echten Server alle mit 404. Statt ein viertes Mal zu raten, liest die App jetzt zwei Dinge: was /media/providers an Endpunkten angibt, und das Programmraster selbst — die Weboberfläche von Plex zeichnet ihren Guide daraus, und jeder Rastereintrag trägt Sendername, Nummer, Logo und Einschaltweg mit sich. Damit kommen Sender und Programm aus einer Abfrage, und ihre Kennungen passen zueinander. Getrennt geholt taten sie das nicht: das Programm landete in keiner Zeile. Dazu die Ansicht, für die das alles da ist — ein Programmraster mit stehender Senderspalte, mitlaufender Zeitachse und einer Jetzt-Linie. Antippen öffnet die Sendung; läuft sie gerade, schaltet ein Knopf um. Die Geometrie steht in GuideLayout und ist geprüft: in einer View wäre sie unsichtbar falsch. Die Trennlinien gehören in die Zeile statt zwischen die Zeilen — Divider ist auf 3x-Schirmen keinen ganzen Punkt hoch, und der Bruchteil summiert sich, bis die angetippte Zeile zum Nachbarsender gehört. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9f9eec1b7f
commit
591cba73a7
@@ -167,6 +167,65 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Der Medien-Block einer Sendung im Raster.
|
||||
///
|
||||
/// Er trägt die Angaben zum Sender mit: das Raster ist bei Plex nicht nur
|
||||
/// eine Liste von Sendungen, sondern die einzige Antwort, in der Sendername,
|
||||
/// Nummer, Logo und Einschaltweg zusammen stehen. Drei geratene
|
||||
/// Senderlisten-Adressen ergaben 404 — diese hier liefert die
|
||||
/// Weboberfläche selbst aus.
|
||||
struct PlexGridMedia: Decodable {
|
||||
let channelIdentifier: String?
|
||||
let channelTitle: String?
|
||||
let channelCallSign: String?
|
||||
let channelThumb: String?
|
||||
let channelVcn: String?
|
||||
let parts: [PlexGridPart]
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case channelIdentifier, channelTitle, channelCallSign, channelThumb, channelVcn
|
||||
case part = "Part"
|
||||
}
|
||||
|
||||
init(from decoder: any Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.channelIdentifier = try? values.decodeIfPresent(String.self, forKey: .channelIdentifier)
|
||||
self.channelTitle = try values.decodeIfPresent(String.self, forKey: .channelTitle)
|
||||
self.channelCallSign = try values.decodeIfPresent(String.self, forKey: .channelCallSign)
|
||||
self.channelThumb = try values.decodeIfPresent(String.self, forKey: .channelThumb)
|
||||
// Die Sendernummer kommt mal als Zahl, mal als Zeichenkette.
|
||||
if let text = try? values.decodeIfPresent(String.self, forKey: .channelVcn) {
|
||||
self.channelVcn = text
|
||||
} else if let number = try? values.decodeIfPresent(Int.self, forKey: .channelVcn) {
|
||||
self.channelVcn = String(number)
|
||||
} else {
|
||||
self.channelVcn = nil
|
||||
}
|
||||
self.parts = try values.decodeIfPresent([PlexGridPart].self, forKey: .part) ?? []
|
||||
}
|
||||
|
||||
/// Der Sender, sofern dieser Block einen benennt.
|
||||
///
|
||||
/// Ohne Namen kein Sender: eine Zeile, die nur „—" hieße, ist für die
|
||||
/// Senderliste wertlos und stünde bei jedem Eintrag doppelt.
|
||||
var channel: PlexChannel? {
|
||||
guard let name = channelTitle ?? channelCallSign else { return nil }
|
||||
let identity = channelIdentifier ?? parts.first?.key ?? name
|
||||
return PlexChannel(
|
||||
id: identity,
|
||||
title: name,
|
||||
channelIdentifier: channelVcn ?? channelIdentifier,
|
||||
thumb: channelThumb,
|
||||
key: parts.first?.key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Der Strom hinter einer Rastersendung.
|
||||
struct PlexGridPart: Decodable {
|
||||
let key: String?
|
||||
}
|
||||
|
||||
/// Ein Sender.
|
||||
public struct PlexChannel: Sendable, Equatable, Decodable, Identifiable {
|
||||
/// Die Nummer, unter der der Sender läuft — etwa `"3.1"`.
|
||||
@@ -295,13 +354,19 @@ struct PlexGridEntry: Decodable {
|
||||
let endsAt: Int?
|
||||
|
||||
/// Die Sendermedien; darin steht, zu welchem Sender die Sendung gehört.
|
||||
let media: [PlexMedia]?
|
||||
let media: [PlexGridMedia]?
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case ratingKey, title, summary, grandparentTitle, thumb, beginsAt, endsAt
|
||||
case media = "Media"
|
||||
}
|
||||
|
||||
/// Der Sender, auf dem diese Sendung läuft.
|
||||
///
|
||||
/// Hier steht der Sender vollständig: Name, Nummer, Logo, Einschaltweg.
|
||||
/// Das ist der Grund, warum das Raster die Senderliste ersetzen kann.
|
||||
var channel: PlexChannel? { media?.compactMap(\.channel).first }
|
||||
|
||||
func program(channelID: String?) -> PlexProgram? {
|
||||
guard let beginsAt, let endsAt, let title else { return nil }
|
||||
return PlexProgram(
|
||||
|
||||
Reference in New Issue
Block a user