iOS: stem playlist refresh and covers, download queue page, reorderable Library, whole-library radio
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7c8dfb54ca
commit
a4c870251c
18 files changed
+514
-30
No files matched your search
@@ -325,6 +325,46 @@ final class PlexServer: MediaSource, @unchecked Sendable {
|
||||
return try JSONDecoder().decode(PlexPin.self, from: try await http.data(req))
|
||||
}
|
||||
|
||||
/// A strong PIN, completed in the browser at [authUrl] (same flow as the web UI).
|
||||
func createStrongPin() async throws -> PlexPin {
|
||||
var req = request(URL(string: "https://plex.tv/api/v2/pins?strong=true")!, withToken: false, method: "POST")
|
||||
req.httpBody = Data()
|
||||
return try JSONDecoder().decode(PlexPin.self, from: try await http.data(req))
|
||||
}
|
||||
|
||||
func authUrl(code: String) -> URL? {
|
||||
var c = URLComponents(string: "https://app.plex.tv/auth")
|
||||
c?.percentEncodedFragment = "?clientID=\(urlEncode(settings.clientId))&code=\(urlEncode(code))&context%5Bdevice%5D%5Bproduct%5D=\(urlEncode(Self.product))"
|
||||
return c?.url
|
||||
}
|
||||
|
||||
/// Every server this plex.tv account can reach, owned or shared.
|
||||
func resources(token: String) async throws -> [PlexResource] {
|
||||
var req = request(URL(string: "https://plex.tv/api/v2/resources?includeHttps=1&includeRelay=1")!, withToken: false)
|
||||
req.setValue(token, forHTTPHeaderField: "X-Plex-Token")
|
||||
let list = try JSONDecoder().decode([Lenient<PlexResource>].self, from: try await http.data(req))
|
||||
return list.compactMap(\.value).filter(\.isServer)
|
||||
}
|
||||
|
||||
/// The first address that answers /identity as the expected server, local ones first, relay last.
|
||||
func reach(_ r: PlexResource, userToken: String) async -> String? {
|
||||
let tok = r.accessToken.isEmpty ? userToken : r.accessToken
|
||||
let ordered = r.connections.filter { !$0.uri.isEmpty }.sorted { rank($0) < rank($1) }
|
||||
for c in ordered {
|
||||
let base = c.uri.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
|
||||
guard let u = URL(string: base + "/identity") else { continue }
|
||||
var req = request(u, withToken: false)
|
||||
req.setValue(tok, forHTTPHeaderField: "X-Plex-Token")
|
||||
req.timeoutInterval = 3.5
|
||||
guard let body = try? await http.data(req),
|
||||
let id = try? decode(body).machineIdentifier, id == r.clientIdentifier || r.clientIdentifier.isEmpty else { continue }
|
||||
return base
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private func rank(_ c: PlexResource.Connection) -> Int { c.relay ? 2 : (c.local ? 0 : 1) }
|
||||
|
||||
func checkPin(_ id: Int64) async throws -> PlexPin {
|
||||
let req = request(URL(string: "https://plex.tv/api/v2/pins/\(id)")!, withToken: false)
|
||||
return try JSONDecoder().decode(PlexPin.self, from: try await http.data(req))
|
||||
|
||||
Reference in new issue
Block a user