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
@@ -197,23 +197,23 @@ struct ChannelPathTests {
|
||||
)
|
||||
|
||||
@Test("Die Kennung der Senderliste wird ausgepackt, nicht roh eingesetzt")
|
||||
func usesBareIdentifier() {
|
||||
func usesBareIdentifier() throws {
|
||||
// Der Fehler, der 404 ergab: die volle Adresse mit Schema, Anhang und
|
||||
// Prozentzeichen landete unverändert im Pfad.
|
||||
let paths = PlexServer.channelPaths(dvr: dvr, epgIdentifier: "epg:2")
|
||||
let first = paths[0]
|
||||
let paths = PlexServer.knownPaths(dvr: dvr, epgIdentifier: "epg:2")
|
||||
let lineupPath = try #require(paths.first { $0.contains("/lineups/") })
|
||||
|
||||
#expect(first == "/epg:2/lineups/6228f7f2364e02fa5ec8e51c/channels")
|
||||
#expect(!first.contains("lineup://"))
|
||||
#expect(!first.contains("#"))
|
||||
#expect(!first.contains("%"))
|
||||
#expect(lineupPath == "/epg:2/lineups/6228f7f2364e02fa5ec8e51c/channels")
|
||||
#expect(!lineupPath.contains("lineup://"))
|
||||
#expect(!lineupPath.contains("#"))
|
||||
#expect(!lineupPath.contains("%"))
|
||||
}
|
||||
|
||||
@Test("Es gibt Rückfalllinien")
|
||||
func offersFallbacks() {
|
||||
// Die Form unterscheidet sich zwischen Serverfassungen, und dieser
|
||||
// Teil der API hat keine Beschreibung.
|
||||
let paths = PlexServer.channelPaths(dvr: dvr, epgIdentifier: "epg:2")
|
||||
let paths = PlexServer.knownPaths(dvr: dvr, epgIdentifier: "epg:2")
|
||||
#expect(paths.count >= 2)
|
||||
#expect(paths.contains("/livetv/dvrs/1/channels"))
|
||||
}
|
||||
@@ -221,7 +221,7 @@ struct ChannelPathTests {
|
||||
@Test("Ohne Senderliste bleiben die übrigen Wege")
|
||||
func withoutLineup() {
|
||||
let bare = PlexDVR(key: "7")
|
||||
let paths = PlexServer.channelPaths(dvr: bare, epgIdentifier: "epg:2")
|
||||
let paths = PlexServer.knownPaths(dvr: bare, epgIdentifier: "epg:2")
|
||||
#expect(!paths.isEmpty)
|
||||
#expect(paths.allSatisfy { !$0.contains("lineups//") })
|
||||
}
|
||||
|
||||
@@ -91,6 +91,63 @@ struct LiveTVGuideTests {
|
||||
#expect(guide.channels.isEmpty)
|
||||
}
|
||||
|
||||
@Test("Die Senderliste des Empfängers wird gefunden, ohne dass sie geraten wird")
|
||||
func sweepsUntilSomethingAnswers() async throws {
|
||||
// Der Fall, der viermal danebenging: die Senderliste steht weder beim
|
||||
// Programmanbieter noch unter dem Empfänger, sondern beim Gerät — und
|
||||
// sie schreibt den Namen in `name` statt in `title`.
|
||||
let device = """
|
||||
{"MediaContainer":{"size":2,"Channel":[
|
||||
{"name":"SRF 1 HD","vcn":1,"key":"/livetv/channels/11","enabled":"1"},
|
||||
{"name":"SRF zwei HD","vcn":2,"key":"/livetv/channels/12","enabled":"1"}
|
||||
]}}
|
||||
"""
|
||||
let server = server(routes: ["/media/grabbers/devices/tuner-7/channels": device])
|
||||
let withTuner = PlexDVR(
|
||||
key: "2",
|
||||
devices: [PlexTunerDevice(key: "tuner-7")],
|
||||
epgIdentifier: "tv.plex.providers.epg.cloud:2"
|
||||
)
|
||||
|
||||
let lookup = await server.channels(inDVR: withTuner)
|
||||
|
||||
#expect(lookup.channels.map(\.title) == ["SRF 1 HD", "SRF zwei HD"])
|
||||
#expect(lookup.channels.first?.channelIdentifier == "1")
|
||||
}
|
||||
|
||||
@Test("Jede abgeklopfte Adresse steht mit ihrer Antwort in der Spur")
|
||||
func recordsWhatEveryPathAnswered() async throws {
|
||||
// Ohne diese Spur ist „keine Sender" eine Sackgasse: sie sagt nicht,
|
||||
// ob der Server nichts hergibt oder die App am falschen Ort sucht.
|
||||
let server = server(routes: [:])
|
||||
let withTuner = PlexDVR(key: "2", epgIdentifier: "tv.plex.providers.epg.cloud:2")
|
||||
|
||||
let lookup = await server.channels(inDVR: withTuner)
|
||||
|
||||
#expect(lookup.channels.isEmpty)
|
||||
#expect(lookup.trace.count >= 3)
|
||||
#expect(lookup.trace.allSatisfy { !$0.result.isEmpty })
|
||||
#expect(lookup.trace.contains { $0.path == "/livetv/dvrs/2/channels" })
|
||||
}
|
||||
|
||||
@Test("Eine Liste aus Strichen zählt nicht als Senderliste")
|
||||
func rejectsNamelessChannels() async throws {
|
||||
// Genau das ist einmal durchgegangen: dreißig Einträge ohne Namen
|
||||
// sahen aus wie eine Senderliste und verdrängten die richtige Quelle.
|
||||
let nameless = """
|
||||
{"MediaContainer":{"size":2,"Channel":[
|
||||
{"channelKey":"a","enabled":"1"},{"channelKey":"b","enabled":"1"}
|
||||
]}}
|
||||
"""
|
||||
let server = server(routes: ["/livetv/dvrs/2/channels": nameless])
|
||||
let withTuner = PlexDVR(key: "2", epgIdentifier: "tv.plex.providers.epg.cloud:2")
|
||||
|
||||
let lookup = await server.channels(inDVR: withTuner)
|
||||
|
||||
#expect(lookup.channels.isEmpty)
|
||||
#expect(lookup.trace.contains { $0.path == "/livetv/dvrs/2/channels" && $0.result.contains("Channel: 2") })
|
||||
}
|
||||
|
||||
@Test("Die Einschaltadresse reist mit dem Sender mit")
|
||||
func keepsTuneKey() async throws {
|
||||
let server = server(routes: [
|
||||
|
||||
Reference in New Issue
Block a user