Live-TV: die Senderliste wird gesucht, nicht geraten — und Serien richtig beschriftet
Vier geratene Adressen, vier 404. Statt ein fünftes Mal zu raten, klopft die App jetzt in einem Durchgang und gleichzeitig ein Dutzend Adressen ab: was /media/providers an Endpunkten angibt, welche Unterverzeichnisse Programmanbieter und Empfänger in ihrer eigenen Antwort führen, die bekannten Formen anderer Serverfassungen, die Senderliste des Tuners selbst — und zuletzt das Programmraster. Dabei zwei Fehlerquellen mitbehoben, die jede einzeln wie „keine Sender" ausgesehen hätten: der Sendername steht je nach Tunertyp in title, name, channelTitle oder callSign, und eine Antwort mit dreißig namenlosen Einträgen sieht aus wie eine Senderliste, ist aber keine. Bleibt es leer, steht jetzt jede abgeklopfte Adresse mit ihrer Antwort in der Ansicht. Das beantwortet „liegt es am Server oder an der App" ohne eine weitere Runde Rückfragen. Dazu drei Sachen bei Serien: - Groß steht, worum es geht — bei einer Folge ihr Titel, bei einer Staffel der Name der Serie. „Staffel 10" als Überschrift sagte nicht, welche Serie gemeint ist. - Die Einordnung steht darunter: Serienname bei Folgen, Staffelnummer bei Staffeln. - Von der Folge führt ein Weg zur Staffel und zur Serie. Wer über „Weiterschauen" bei einer Folge landete, saß dort bisher fest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
591cba73a7
commit
7e36f37506
@@ -135,13 +135,15 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable {
|
||||
public let key: String?
|
||||
public let uuid: String?
|
||||
public let title: String?
|
||||
/// Die Gerätekennung — unter ihr führt der Server die gefundenen Sender.
|
||||
public let deviceIdentifier: String?
|
||||
/// Welche Sender der Senderliste hier eingeschaltet sind.
|
||||
public let channelMappings: [PlexChannelMapping]
|
||||
|
||||
public var id: String { uuid ?? key ?? title ?? "tuner" }
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case key, uuid, title
|
||||
case key, uuid, title, deviceId, deviceIdentifier
|
||||
case channelMapping = "ChannelMapping"
|
||||
}
|
||||
|
||||
@@ -150,6 +152,9 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable {
|
||||
self.key = try? values.decodeIfPresent(String.self, forKey: .key)
|
||||
self.uuid = try values.decodeIfPresent(String.self, forKey: .uuid)
|
||||
self.title = try values.decodeIfPresent(String.self, forKey: .title)
|
||||
self.deviceIdentifier = (try? values.decodeIfPresent(String.self, forKey: .deviceId))
|
||||
?? (try? values.decodeIfPresent(String.self, forKey: .deviceIdentifier))
|
||||
?? nil
|
||||
self.channelMappings = try values
|
||||
.decodeIfPresent([PlexChannelMapping].self, forKey: .channelMapping) ?? []
|
||||
}
|
||||
@@ -158,11 +163,13 @@ public struct PlexTunerDevice: Sendable, Equatable, Decodable, Identifiable {
|
||||
key: String? = nil,
|
||||
uuid: String? = nil,
|
||||
title: String? = nil,
|
||||
deviceIdentifier: String? = nil,
|
||||
channelMappings: [PlexChannelMapping] = []
|
||||
) {
|
||||
self.key = key
|
||||
self.uuid = uuid
|
||||
self.title = title
|
||||
self.deviceIdentifier = deviceIdentifier
|
||||
self.channelMappings = channelMappings
|
||||
}
|
||||
}
|
||||
@@ -236,16 +243,32 @@ public struct PlexChannel: Sendable, Equatable, Decodable, Identifiable {
|
||||
public let key: String?
|
||||
public let id: String
|
||||
|
||||
/// Ob dieser Sender einen Namen trägt.
|
||||
///
|
||||
/// Eine Liste aus dreißig Strichen sieht aus wie eine Senderliste, ist
|
||||
/// aber keine — dieser Irrtum hat hier schon einmal eine falsche Quelle
|
||||
/// als die richtige durchgehen lassen.
|
||||
public var hasName: Bool { title != Self.placeholder }
|
||||
|
||||
static let placeholder = "—"
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case channelIdentifier, title, thumb, key
|
||||
case identifier = "id"
|
||||
case uuid
|
||||
case uuid, name, callSign, vcn, number, channelTitle, channelCallSign
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.title = try values.decodeIfPresent(String.self, forKey: .title) ?? "—"
|
||||
self.channelIdentifier = try values.decodeIfPresent(String.self, forKey: .channelIdentifier)
|
||||
// Vier Schreibweisen für denselben Namen, je nach Tunertyp und
|
||||
// Anbieter. Nur `title` zu lesen hieße, die halbe Senderliste als
|
||||
// namenlos zu verwerfen.
|
||||
self.title = [CodingKeys.title, .name, .channelTitle, .callSign, .channelCallSign]
|
||||
.lazy
|
||||
.compactMap { try? values.decodeIfPresent(String.self, forKey: $0) }
|
||||
.first { !$0.isEmpty } ?? Self.placeholder
|
||||
|
||||
self.channelIdentifier = Self.text(in: values, forKeys: [.channelIdentifier, .vcn, .number])
|
||||
self.thumb = try values.decodeIfPresent(String.self, forKey: .thumb)
|
||||
self.key = try values.decodeIfPresent(String.self, forKey: .key)
|
||||
|
||||
@@ -283,6 +306,22 @@ public struct PlexChannel: Sendable, Equatable, Decodable, Identifiable {
|
||||
return keys.contains { $0.split(separator: "/").last.map(String.init) == id }
|
||||
}
|
||||
|
||||
/// Ein Wert, der als Zahl oder als Zeichenkette kommen kann.
|
||||
private static func text(
|
||||
in values: KeyedDecodingContainer<CodingKeys>,
|
||||
forKeys keys: [CodingKeys]
|
||||
) -> String? {
|
||||
for key in keys {
|
||||
if let text = try? values.decodeIfPresent(String.self, forKey: key), !text.isEmpty {
|
||||
return text
|
||||
}
|
||||
if let number = try? values.decodeIfPresent(Int.self, forKey: key) {
|
||||
return String(number)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public init(id: String, title: String, channelIdentifier: String? = nil, thumb: String? = nil, key: String? = nil) {
|
||||
self.id = id
|
||||
self.title = title
|
||||
|
||||
Reference in New Issue
Block a user