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:
@@ -0,0 +1,119 @@
|
||||
import Foundation
|
||||
import Testing
|
||||
@testable import PlexKit
|
||||
|
||||
@Suite("Schriftzüge und Bildliste")
|
||||
struct ClearLogoTests {
|
||||
private func item(from json: String) throws -> PlexItem {
|
||||
let data = Data(#"{"MediaContainer":{"Metadata":[\#(json)]}}"#.utf8)
|
||||
return try #require(
|
||||
try PlexResponse.decode(PlexMediaContainer<PlexItem>.self, from: data).content.first
|
||||
)
|
||||
}
|
||||
|
||||
@Test("Der Schriftzug wird aus der Bildliste gelesen")
|
||||
func findsClearLogo() throws {
|
||||
let decoded = try item(from: """
|
||||
{
|
||||
"ratingKey": "1", "type": "movie", "title": "Harry Potter",
|
||||
"Image": [
|
||||
{"alt": "Harry Potter", "type": "coverPoster", "url": "/library/metadata/1/thumb/1"},
|
||||
{"alt": "Harry Potter", "type": "background", "url": "/library/metadata/1/art/1"},
|
||||
{"alt": "Harry Potter", "type": "clearLogo", "url": "/library/metadata/1/clearLogo/1"}
|
||||
]
|
||||
}
|
||||
""")
|
||||
|
||||
#expect(decoded.clearLogoPath == "/library/metadata/1/clearLogo/1")
|
||||
}
|
||||
|
||||
@Test("Ohne Schriftzug bleibt es beim Titel")
|
||||
func withoutClearLogo() throws {
|
||||
// Der Normalfall bei selbst zusammengestellten Mediatheken.
|
||||
let decoded = try item(from: """
|
||||
{
|
||||
"ratingKey": "1", "type": "movie", "title": "Ein Film",
|
||||
"Image": [{"type": "coverPoster", "url": "/library/metadata/1/thumb/1"}]
|
||||
}
|
||||
""")
|
||||
#expect(decoded.clearLogoPath == nil)
|
||||
}
|
||||
|
||||
@Test("Eine fehlende Bildliste ist kein Fehler")
|
||||
func missingImageList() throws {
|
||||
// Listen liefern keine Bildliste mit — nur die Einzelabfrage tut das.
|
||||
let decoded = try item(from: #"{"ratingKey": "1", "type": "movie", "title": "Ein Film"}"#)
|
||||
#expect(decoded.images.isEmpty)
|
||||
#expect(decoded.clearLogoPath == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@Suite("Empfänger und Sender")
|
||||
struct TunerDecodingTests {
|
||||
|
||||
@Test("Sender werden aus dem Empfänger gelesen")
|
||||
func readsChannelsFromDevice() throws {
|
||||
// Der Fall, an dem „Keine Sender" hing: Plex liefert sie verschachtelt
|
||||
// in der Antwort auf /livetv/dvrs mit, nicht über einen eigenen
|
||||
// Endpunkt.
|
||||
let json = """
|
||||
{"MediaContainer":{"Dvr":[{
|
||||
"key": "1", "lineupTitle": "Kabel",
|
||||
"Device": [{
|
||||
"key": "2", "uuid": "tuner-1", "title": "HDHomeRun",
|
||||
"Channel": [
|
||||
{"id": 10, "channelIdentifier": "1.1", "title": "SRF 1 HD", "key": "/livetv/channel/1"},
|
||||
{"id": 11, "channelIdentifier": "1.2", "title": "SRF zwei"}
|
||||
]
|
||||
}]
|
||||
}]}}
|
||||
"""
|
||||
let container = try PlexResponse.decode(
|
||||
PlexMediaContainer<PlexDVR>.self,
|
||||
from: Data(json.utf8)
|
||||
)
|
||||
let dvr = try #require(container.content.first)
|
||||
|
||||
#expect(dvr.devices.count == 1)
|
||||
#expect(dvr.channels.count == 2)
|
||||
#expect(dvr.channels.first?.title == "SRF 1 HD")
|
||||
#expect(dvr.channels.first?.channelIdentifier == "1.1")
|
||||
}
|
||||
|
||||
@Test("Auch die ältere Schreibweise ChannelMapping wird gelesen")
|
||||
func readsChannelMapping() throws {
|
||||
let json = """
|
||||
{"MediaContainer":{"Dvr":[{
|
||||
"key": "1",
|
||||
"Device": [{"uuid": "t", "ChannelMapping": [{"id": "a", "title": "3sat"}]}]
|
||||
}]}}
|
||||
"""
|
||||
let container = try PlexResponse.decode(
|
||||
PlexMediaContainer<PlexDVR>.self,
|
||||
from: Data(json.utf8)
|
||||
)
|
||||
#expect(container.content.first?.channels.first?.title == "3sat")
|
||||
}
|
||||
|
||||
@Test("Ein Empfänger ohne Sender ergibt eine leere Liste, keinen Fehler")
|
||||
func deviceWithoutChannels() throws {
|
||||
let json = #"{"MediaContainer":{"Dvr":[{"key":"1","Device":[{"uuid":"t"}]}]}}"#
|
||||
let container = try PlexResponse.decode(
|
||||
PlexMediaContainer<PlexDVR>.self,
|
||||
from: Data(json.utf8)
|
||||
)
|
||||
#expect(container.content.first?.channels.isEmpty == true)
|
||||
}
|
||||
|
||||
@Test("Ein Empfänger-Schlüssel als Zahl wird gelesen")
|
||||
func numericDVRKey() throws {
|
||||
// Je nach Serverfassung kommt er als Zahl oder als Zeichenkette. Ein
|
||||
// fester Typ hätte die ganze Empfängerliste unlesbar gemacht.
|
||||
let json = #"{"MediaContainer":{"Dvr":[{"key":42}]}}"#
|
||||
let container = try PlexResponse.decode(
|
||||
PlexMediaContainer<PlexDVR>.self,
|
||||
from: Data(json.utf8)
|
||||
)
|
||||
#expect(container.content.first?.key == "42")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user