import contextlib
import json
from http import HTTPStatus
from time import sleep, time
import requests
import xbmcgui
from lib.api.plex.models.plex_models import AuthPin, PlexUser
from lib.api.plex.settings import settings
from lib.api.plex.utils import HTTPException, PlexUnauthorizedError
from lib.utils.kodi.utils import copy2clip, dialog_ok, kodilog, set_setting
class PlexApi:
def __init__(self) -> None:
self.PLEX_AUTH_URL = "https://app.plex.tv/auth#?"
self.PLEX_API_URL = "https://plex.tv/api/v2"
self.client = requests.Session()
self.headers = {"accept": "application/json"}
self.auth_token = None
def login(self):
auth_pin = self.create_auth_pin()
copy2clip(auth_pin.code)
content = "{}[CR]{}".format(
"Navigate to: [B]https://www.plex.tv/link[/B]",
f"and enter the code: [COLOR seagreen][B]{auth_pin.code}[/B][/COLOR] ",
)
progressDialog = xbmcgui.DialogProgress()
progressDialog.create("Plex Auth")
progressDialog.update(-1, content)
kodilog("Start polling plex.tv for token")
start_time = time()
while time() - start_time < 300:
auth_token = self.get_auth_token(auth_pin)
if auth_token is not None:
self.auth_token = auth_token
set_setting("plex_token", self.auth_token)
progressDialog.close()
dialog_ok("Success", "Authentication completed.")
return True
if progressDialog.iscanceled():
progressDialog.close()
return False
sleep(1)
else:
progressDialog.close()
dialog_ok("Error:", "Pin timed out.")
return False
def create_auth_pin(self) -> AuthPin:
response = self.client.post(
f"{self.PLEX_API_URL}/pins",
data={
"strong": "false",
"X-Plex-Product": settings.product_name,
"X-Plex-Client-Identifier": settings.identifier,
},
headers=self.headers,
timeout=settings.plex_requests_timeout,
)
json = response.json()
return AuthPin(**json)
def get_auth_token(self, auth_pin):
json = self.get_json(
url=f"{self.PLEX_API_URL}/pins/{auth_pin.id}",
params={
"code": auth_pin.code,
"X-Plex-Client-Identifier": settings.identifier,
},
)
return json["authToken"]
def get_json(self, url, params=None):
if params is None:
params = {}
try:
response = self.client.get(
url,
params=params,
headers=self.headers,
timeout=settings.plex_requests_timeout,
)
if response.status_code in (401, 403):
raise PlexUnauthorizedError()
if response.status_code >= 400:
raise HTTPException(
status_code=502,
detail="Received error from plex server",
)
return json.loads(response.content)
except TimeoutError:
raise HTTPException(
status_code=504,
detail="Plex server timeout error",
) from None
def get_plex_user(self):
response = self.client.get(
f"{self.PLEX_API_URL}/user",
params={
"X-Plex-Product": settings.product_name,
"X-Plex-Client-Identifier": settings.identifier,
"X-Plex-Token": self.auth_token,
},
headers=self.headers,
timeout=settings.plex_requests_timeout,
)
if response.status_code != HTTPStatus.OK:
return
json = response.json()
return PlexUser(**json)
def logout(self):
self.auth_token = ""
set_setting("plex_token", "")
with contextlib.suppress(BaseException):
self.client.close()
set_setting("plex_user", "")
set_setting("plex_server_name", "")
set_setting("plex_discovery_url", "")
set_setting("plex_streaming_url", "")
set_setting("plex_token", "")
set_setting("plex_server_token", "")