Live-TV-Sender gefunden, Film-Schriftzüge ergänzt
Senderliste
Plex liefert die Sender verschachtelt in der Antwort auf /livetv/dvrs
mit — jeder Empfänger bringt seine eigenen unter `Device` → `Channel`.
Mein getrennter Endpunkt /livetv/dvrs/{id}/channels war eine Vermutung
und lieferte nichts. Bei einem Server mit über dreißig eingerichteten
Sendern stand deshalb „Keine Sender".
Die verschachtelte Liste hat jetzt Vorrang, der Endpunkt bleibt als
Rückfalllinie. `ChannelMapping` wird als ältere Schreibweise ebenfalls
gelesen, und der Empfänger-Schlüssel kommt je nach Serverfassung als
Zahl oder als Zeichenkette.
Bleibt die Liste leer, sagt die Ansicht jetzt welcher Weg was ergeben
hat. In einem Bereich ohne offizielle Beschreibung ist das die Angabe,
mit der sich der nächste Fehler finden lässt.
Schriftzüge
Plex liefert bei vielen Filmen ein `clearLogo` — den Schriftzug in
seiner eigenen Type, freigestellt. Der tritt jetzt im Aufmacher an die
Stelle des gesetzten Titels.
Dabei ein Fallstrick: /photo/:/transcode liefert JPEG, und JPEG kennt
keine Transparenz. Über den Bild-Transcoder geladen wäre aus dem
freigestellten Logo ein schwarzer Kasten geworden. Es wird deshalb
unverändert geladen. Fehlt der Schriftzug oder scheitert das Laden,
bleibt es beim Titel.
161 Tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,13 +31,34 @@ extension PlexServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Die Sender eines Empfängers.
|
||||
public func channels(inDVR dvr: PlexDVR) async throws(PlexError) -> [PlexChannel] {
|
||||
let container = try await client.send(
|
||||
PlexRequest(path: "/livetv/dvrs/\(dvr.key)/channels"),
|
||||
as: PlexMediaContainer<PlexChannel>.self
|
||||
)
|
||||
return container.content
|
||||
/// Die Sender eines Empfängers, samt Auskunft darüber, woher sie kamen.
|
||||
///
|
||||
/// Zwei Quellen, in dieser Reihenfolge. Die erste ist die verlässliche:
|
||||
/// Plex liefert die Sender bereits verschachtelt in der Antwort auf
|
||||
/// `/livetv/dvrs` mit. Der getrennte Endpunkt war meine erste Vermutung
|
||||
/// und lieferte nichts — daran hing „Keine Sender" bei einem Server mit
|
||||
/// über dreißig eingerichteten Sendern.
|
||||
///
|
||||
/// Die Auskunft über die Quelle steht in der Diagnose. Bei einem Bereich
|
||||
/// ohne offizielle Beschreibung ist „welcher Weg hat gegriffen" die
|
||||
/// Angabe, mit der sich der nächste Fehler finden lässt.
|
||||
public func channels(inDVR dvr: PlexDVR) async -> ChannelLookup {
|
||||
if !dvr.channels.isEmpty {
|
||||
return ChannelLookup(channels: dvr.channels, source: .embeddedInDVR)
|
||||
}
|
||||
|
||||
do {
|
||||
let container = try await client.send(
|
||||
PlexRequest(path: "/livetv/dvrs/\(dvr.key)/channels"),
|
||||
as: PlexMediaContainer<PlexChannel>.self
|
||||
)
|
||||
return ChannelLookup(
|
||||
channels: container.content,
|
||||
source: container.content.isEmpty ? .nothingFound : .separateEndpoint
|
||||
)
|
||||
} catch {
|
||||
return ChannelLookup(channels: [], source: .failed(String(describing: error)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Die Kennung des Programmanbieters.
|
||||
@@ -116,6 +137,37 @@ extension PlexServer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Woher die Senderliste kam.
|
||||
public struct ChannelLookup: Sendable {
|
||||
public enum Source: Sendable, Equatable {
|
||||
/// Aus der Antwort auf `/livetv/dvrs` — der übliche Weg.
|
||||
case embeddedInDVR
|
||||
/// Über `/livetv/dvrs/{id}/channels`.
|
||||
case separateEndpoint
|
||||
/// Beide Wege lieferten nichts.
|
||||
case nothingFound
|
||||
case failed(String)
|
||||
}
|
||||
|
||||
public let channels: [PlexChannel]
|
||||
public let source: Source
|
||||
|
||||
public init(channels: [PlexChannel], source: Source) {
|
||||
self.channels = channels
|
||||
self.source = source
|
||||
}
|
||||
|
||||
/// Kurzbeschreibung für die Diagnose.
|
||||
public var description: String {
|
||||
switch source {
|
||||
case .embeddedInDVR: "\(channels.count) Sender, aus der Empfängerliste"
|
||||
case .separateEndpoint: "\(channels.count) Sender, über /livetv/dvrs/…/channels"
|
||||
case .nothingFound: "Kein Weg lieferte Sender"
|
||||
case .failed(let reason): "Abfrage gescheitert: \(reason)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Ein Anbieter am Server — Mediathek, Programmführer, Entdecken.
|
||||
public struct PlexMediaProvider: Sendable, Equatable, Decodable, Identifiable {
|
||||
public let identifier: String
|
||||
|
||||
@@ -156,6 +156,25 @@ public actor PlexServer {
|
||||
return client.artworkURL(path: path, width: width, height: height, token: token)
|
||||
}
|
||||
|
||||
/// Ein Bild unverändert, ohne den Umweg über den Bild-Transcoder.
|
||||
///
|
||||
/// Für freigestellte Schriftzüge zwingend: `/photo/:/transcode` liefert
|
||||
/// JPEG, und JPEG kennt keine Transparenz. Ein Logo käme als weißer
|
||||
/// Schriftzug auf schwarzem Kasten heraus statt freigestellt auf dem
|
||||
/// Hintergrundbild.
|
||||
public nonisolated func rawImageURL(path: String?) -> URL? {
|
||||
guard let path, !path.isEmpty,
|
||||
let baseURL = connection.url,
|
||||
var components = URLComponents(
|
||||
url: baseURL.appending(path: path),
|
||||
resolvingAgainstBaseURL: false
|
||||
)
|
||||
else { return nil }
|
||||
|
||||
components.queryItems = [URLQueryItem(name: "X-Plex-Token", value: token)]
|
||||
return components.url
|
||||
}
|
||||
|
||||
/// Die Adresse für DirectPlay: die Datei, unverändert.
|
||||
public nonisolated func directPlayURL(part: PlexPart) -> URL? {
|
||||
streamURL(partKey: part.key, download: false)
|
||||
|
||||
Reference in New Issue
Block a user