Files
Flaro/Packages/FlixyKit/Sources/FlixyProbe/main.swift
T
ScarriffleandClaude Opus 5 03a8d1f899 Ein Werkzeug für die Fragen, die reines HTTP sind
Das Upload-Kontingent des Tages ist aufgebraucht — verbraucht von meinen
eigenen Vermutungen über eine Schnittstelle, die keine Oberfläche braucht.
Jede Runde ging als TestFlight-Build hinaus und kam als Screenshot zurück.

`swift run FlixyProbe` macht dasselbe in Sekunden: anmelden, Server
suchen, Senderliste und Programm lesen, das Einschalten durchprobieren —
mit derselben Logik wie die App. Der Token kommt über die übliche
Anmeldung und liegt im Schlüsselbund dieses Rechners; gedruckt werden
Pfade, Kennungen und Antworten, keine vollständigen Adressen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 15:04:00 +02:00

107 lines
3.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Foundation
import PlexKit
// Ein Werkzeug für die Fragen, die reines HTTP sind.
//
// Der Grund, dass es das gibt: die Live-TV-Schnittstelle von Plex ist
// nirgends beschrieben, und jede Vermutung darüber ging bisher als
// TestFlight-Build zum Nutzer und kam einen halben Tag später als Screenshot
// zurück. Irgendwann war das Upload-Kontingent des Tages aufgebraucht — für
// eine Frage, die keine Oberfläche braucht.
//
// Der Token wird über die übliche Anmeldung geholt und liegt im Schlüsselbund
// dieses Rechners. Er steht nirgends in der Ausgabe: gedruckt werden Pfade,
// Kennungen und Antworten, keine vollständigen Adressen.
let identity = PlexClientIdentity(
product: "Flixy-Probe",
version: "1.0",
clientIdentifier: storedClientIdentifier(),
platform: "macOS",
platformVersion: ProcessInfo.processInfo.operatingSystemVersionString,
device: "Mac",
deviceName: Host.current().localizedName ?? "Mac"
)
let store = KeychainTokenStore(service: "com.scarriffleservices.flixy.probe")
let authenticator = PlexAuthenticator(identity: identity, tokenStore: store)
let token: String
if let existing = authenticator.storedToken {
print("Angemeldet (Token aus dem Schlüsselbund).")
token = existing
} else {
let pin = try await authenticator.requestPin()
print("""
Anmelden: https://plex.tv/link
Code: \(pin.code)
Warte …
""")
var found: String?
for _ in 0..<150 where found == nil {
try await Task.sleep(for: .seconds(2))
found = try await authenticator.token(for: pin)
}
guard let found else { fatalError("Anmeldung abgelaufen.") }
token = found
print("Angemeldet.\n")
}
let directory = PlexServerDirectory(identity: identity, token: token)
let servers = try await directory.servers()
guard let resource = servers.first else { fatalError("Kein Server am Konto.") }
let selector = PlexConnectionSelector(identity: identity)
guard let connection = await selector.selectConnection(for: resource),
let server = PlexServer(resource: resource, connection: connection, identity: identity)
else { fatalError("Kein Weg zu \(resource.name) erreichbar.") }
print("Server: \(resource.name) über \(connection.local ? "lokal" : "entfernt")\n")
// MARK: - Live-TV
let availability = await server.liveTVAvailability()
guard case .available(let dvrs) = availability, let dvr = dvrs.first else {
print("Live-TV: \(availability)")
exit(0)
}
print("Empfänger: \(dvr.key), Programmführer: \(dvr.epgIdentifier ?? "–")")
let guide = await server.liveTVGuide(dvr: dvr, hours: 2)
print("Sender: \(guide.channels.count) — \(guide.source)\n")
guard let channel = guide.channels.first else { exit(0) }
let program = guide.programs[channel.id]?.first { $0.isRunning() }
print("Einschalten: \(channel.title) (\(channel.channelIdentifier ?? "–"))")
print("Läuft: \(program?.title ?? "–")\n")
let result = await server.tune(
channel: channel,
program: program,
dvr: dvr,
maximumBitrate: nil
)
for probe in result.trace {
print(probe.path)
print(" → \(probe.result)")
}
print("\n" + (result.url == nil ? "Kein Weg trug." : "Erfolg."))
/// Eine Kennung, die über Läufe hinweg gleich bleibt.
///
/// Wechselt sie, gilt die Probe am Konto als neues Gerät, und in der
/// Geräteliste sammelt sich Müll an.
func storedClientIdentifier() -> String {
let key = "flixy.probe.clientIdentifier"
if let stored = UserDefaults.standard.string(forKey: key) { return stored }
let created = UUID().uuidString
UserDefaults.standard.set(created, forKey: key)
return created
}