import { ref } from "vue"; import { CLIENT_IDENTIFIER, APP_NAME } from "./usePlexApi"; export function usePlexAuth() { const loading = ref(false); const plexPopup = ref(null); const pollInterval = ref(null); // Generate a PIN for Plex OAuth async function generatePlexPin() { try { const url = "https://plex.tv/api/v2/pins?strong=true"; const options = { method: "POST", headers: { accept: "application/json", "X-Plex-Product": APP_NAME, "X-Plex-Client-Identifier": CLIENT_IDENTIFIER } }; const response = await fetch(url, options); if (!response.ok) throw new Error("Failed to generate PIN"); const data = await response.json(); return { id: data.id, code: data.code }; } catch (error) { console.error("[PlexAuth] Error generating PIN:", error); return null; } } // Check PIN status async function checkPin(pinId: number, pinCode: string) { try { const url = `https://plex.tv/api/v2/pins/${pinId}?code=${pinCode}`; const options = { headers: { accept: "application/json", "X-Plex-Client-Identifier": CLIENT_IDENTIFIER } }; const response = await fetch(url, options); if (!response.ok) return null; const data = await response.json(); return data.authToken; } catch (error) { console.error("[PlexAuth] Error checking PIN:", error); return null; } } // Construct auth URL function constructAuthUrl(pinCode: string) { const params = new URLSearchParams({ clientID: CLIENT_IDENTIFIER, code: pinCode, "context[device][product]": APP_NAME }); return `https://app.plex.tv/auth#?${params.toString()}`; } // Start polling for PIN function startPolling( pinId: number, pinCode: string, onSuccess: (token: string) => void ) { pollInterval.value = window.setInterval(async () => { const authToken = await checkPin(pinId, pinCode); if (authToken) { stopPolling(); if (plexPopup.value && !plexPopup.value.closed) { plexPopup.value.close(); } onSuccess(authToken); } }, 1000); } // Stop polling function stopPolling() { if (pollInterval.value) { clearInterval(pollInterval.value); pollInterval.value = null; } } // Set cookie function setPlexAuthCookie(authToken: string) { const expires = new Date(); expires.setDate(expires.getDate() + 30); const domain = window.location.hostname; document.cookie = `plex_auth_token=${authToken}; domain=.${domain}; path=/; expires=${expires.toUTCString()}; SameSite=Strict`; } // Get cookie function getPlexAuthCookie(): string | null { const key = "plex_auth_token"; const value = `; ${document.cookie}`; const parts = value.split(`; ${key}=`); if (parts.length === 2) { return parts.pop()?.split(";").shift() || null; } return null; } // Open authentication popup async function openAuthPopup( onSuccess: (token: string) => void, onError: (msg: string) => void ) { loading.value = true; const width = 600; const height = 700; const left = window.screen.width / 2 - width / 2; const top = window.screen.height / 2 - height / 2; plexPopup.value = window.open( "about:blank", "PlexAuth", `width=${width},height=${height},left=${left},top=${top}` ); if (!plexPopup.value) { onError("Please allow popups for this site to authenticate with Plex"); loading.value = false; return; } // Add loading screen if (plexPopup.value.document) { plexPopup.value.document.write(` Connecting to Plex...

Connecting to Plex...

`); } const pin = await generatePlexPin(); if (!pin) { if (plexPopup.value && !plexPopup.value.closed) plexPopup.value.close(); onError("Could not generate Plex authentication PIN"); loading.value = false; return; } const authUrl = constructAuthUrl(pin.code); if (plexPopup.value && !plexPopup.value.closed) { plexPopup.value.location.href = authUrl; } else { onError("Authentication window was closed"); loading.value = false; return; } startPolling(pin.id, pin.code, onSuccess); // Check if popup closed const popupChecker = setInterval(() => { if (plexPopup.value && plexPopup.value.closed) { clearInterval(popupChecker); stopPolling(); if (loading.value) { loading.value = false; // onError("Plex authentication window was closed"); } } }, 500); } // Cleanup function cleanup() { stopPolling(); if (plexPopup.value && !plexPopup.value.closed) { plexPopup.value.close(); } } return { loading, setPlexAuthCookie, getPlexAuthCookie, openAuthPopup, cleanup }; }