import { VERSION } from "../../../constants"; export interface PlexPinResponse { // Only has the fields we care about authToken?: string; code: string; id: number; } export interface PlexResource { name: string; provides: string; clientIdentifier: string; owned: boolean; } export const DEFAULT_HEADERS = { "Accept": "application/json", "Content-Type": "application/json", "X-Plex-Product": "authentik", "X-Plex-Version": VERSION, "X-Plex-Device-Vendor": "BeryJu.org", }; export function popupCenterScreen(url: string, title: string, w: number, h: number): Window | null { const top = (screen.height - h) / 4, left = (screen.width - w) / 2; const popup = window.open(url, title, `scrollbars=yes,width=${w},height=${h},top=${top},left=${left}`); return popup; } export class PlexAPIClient { token: string; constructor(token: string) { this.token = token; } static async getPin(clientIdentifier: string): Promise<{ authUrl: string, pin: PlexPinResponse }> { const headers = { ...DEFAULT_HEADERS, ...{ "X-Plex-Client-Identifier": clientIdentifier }}; const pinResponse = await fetch("https://plex.tv/api/v2/pins.json?strong=true", { method: "POST", headers: headers }); const pin: PlexPinResponse = await pinResponse.json(); return { authUrl: `https://app.plex.tv/auth#!?clientID=${encodeURIComponent(clientIdentifier)}&code=${pin.code}`, pin: pin }; } static async pinStatus(clientIdentifier: string, id: number): Promise { const headers = { ...DEFAULT_HEADERS, ...{ "X-Plex-Client-Identifier": clientIdentifier }}; const pinResponse = await fetch(`https://plex.tv/api/v2/pins/${id}`, { headers: headers }); const pin: PlexPinResponse = await pinResponse.json(); return pin.authToken || ""; } static async pinPoll(clientIdentifier: string, id: number): Promise { const executePoll = async ( resolve: (authToken: string) => void, reject: (e: Error) => void ) => { try { const response = await PlexAPIClient.pinStatus(clientIdentifier, id); if (response) { resolve(response); } else { setTimeout(executePoll, 500, resolve, reject); } } catch (e) { reject(e); } }; return new Promise(executePoll); } async getServers(): Promise { const resourcesResponse = await fetch(`https://plex.tv/api/v2/resources?X-Plex-Token=${this.token}&X-Plex-Client-Identifier=authentik`, { headers: DEFAULT_HEADERS }); const resources: PlexResource[] = await resourcesResponse.json(); return resources.filter(r => { return r.provides.toLowerCase().includes("server") && r.owned; }); } }