111 lines
3 KiB
Python
111 lines
3 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import urllib.error
|
|
import urllib.request
|
|
from typing import Any
|
|
|
|
from .core import clean_error
|
|
|
|
|
|
def worker_base_url() -> str:
|
|
return os.getenv("WATCHPARTY_WORKER_URL", "").strip().rstrip("/")
|
|
|
|
|
|
def worker_token() -> str:
|
|
return os.getenv("WATCHPARTY_WORKER_TOKEN", "").strip()
|
|
|
|
|
|
def worker_enabled() -> bool:
|
|
return bool(worker_base_url() and worker_token())
|
|
|
|
|
|
def worker_request(method: str, path: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
base_url = worker_base_url()
|
|
token = worker_token()
|
|
if not base_url or not token:
|
|
raise RuntimeError("Watch-party worker is not configured")
|
|
|
|
body = None
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Accept": "application/json",
|
|
}
|
|
if payload is not None:
|
|
body = json.dumps(payload).encode("utf-8")
|
|
headers["Content-Type"] = "application/json"
|
|
|
|
request = urllib.request.Request(
|
|
f"{base_url}{path}",
|
|
data=body,
|
|
headers=headers,
|
|
method=method,
|
|
)
|
|
try:
|
|
with urllib.request.urlopen(request, timeout=20) as response:
|
|
data = response.read()
|
|
if not data:
|
|
return {}
|
|
return json.loads(data.decode("utf-8"))
|
|
except urllib.error.HTTPError as exc:
|
|
detail = exc.read().decode("utf-8", errors="ignore")
|
|
raise RuntimeError(f"Worker API failed: HTTP {exc.code} {detail}") from exc
|
|
except (urllib.error.URLError, TimeoutError) as exc:
|
|
raise RuntimeError(f"Worker API failed: {clean_error(exc)}") from exc
|
|
|
|
|
|
def worker_health() -> dict[str, Any]:
|
|
return worker_request("GET", "/health")
|
|
|
|
|
|
def worker_create_session(*, session_id: int, guild_id: str, voice_channel_id: str, text_channel_id: str) -> dict[str, Any]:
|
|
return worker_request(
|
|
"POST",
|
|
"/sessions",
|
|
{
|
|
"sessionId": str(session_id),
|
|
"guildId": guild_id,
|
|
"voiceChannelId": voice_channel_id,
|
|
"textChannelId": text_channel_id,
|
|
},
|
|
)
|
|
|
|
|
|
def worker_play(
|
|
*,
|
|
session_id: int,
|
|
queue_entry_id: int,
|
|
jellyfin_source_id: str,
|
|
title: str,
|
|
media_type: str,
|
|
playback: dict[str, Any],
|
|
position_seconds: int = 0,
|
|
) -> dict[str, Any]:
|
|
return worker_request(
|
|
"POST",
|
|
f"/sessions/{session_id}/play",
|
|
{
|
|
"queueEntryId": queue_entry_id,
|
|
"jellyfinSourceId": jellyfin_source_id,
|
|
"title": title,
|
|
"mediaType": media_type,
|
|
"playback": playback,
|
|
"positionSeconds": position_seconds,
|
|
},
|
|
)
|
|
|
|
|
|
def worker_control(*, session_id: int, action: str, data: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
return worker_request(
|
|
"POST",
|
|
f"/sessions/{session_id}/control",
|
|
{
|
|
"action": action,
|
|
"data": data or {},
|
|
},
|
|
)
|
|
|
|
|
|
def worker_status(session_id: int) -> dict[str, Any]:
|
|
return worker_request("GET", f"/sessions/{session_id}")
|