Something went wrong. Try again.
experiments of a tiny cytube-like player with yt-dlp
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990import { type RoomState, type ChatMessage, type ClientMessage } from "./types";
export interface WsCallbacks { onState: (state: RoomState) => void; onChat: (msg: ChatMessage) => void; onDisconnect: () => void; onError: (reason: string) => void;}
/// Distinguish a state snapshot from a chat message.function parseServerMessage( data: unknown,): { kind: "state"; data: RoomState } | { kind: "chat"; data: ChatMessage } | null { if (!data || typeof data !== "object") return null;
// State snapshots always have `room_name` and `clients`. if ("room_name" in data && typeof (data as Record<string, unknown>).room_name === "string") { return { kind: "state", data: data as RoomState }; }
// Chat messages always have `user_name` and `content`. if ("user_name" in data && "content" in data) { return { kind: "chat", data: data as ChatMessage }; }
return null;}
/// Connect to a room's WebSocket. Returns a send function.export function connectWs( roomId: string, userName: string, callbacks: WsCallbacks,): (msg: ClientMessage) => void { const protocol = location.protocol === "https:" ? "wss:" : "ws:"; const url = `${protocol}//${location.host}/api/v1/ws/${roomId}?name=${encodeURIComponent(userName)}`; let ws: WebSocket | null = new WebSocket(url); let sendQueue: string[] = []; let opened = false;
function send(raw: string) { if (ws?.readyState === WebSocket.OPEN) { ws.send(raw); } else { sendQueue.push(raw); } }
ws.onopen = () => { opened = true; for (const msg of sendQueue) { ws?.send(msg); } sendQueue = []; };
ws.onmessage = (event) => { if (typeof event.data !== "string") return; try { const obj = JSON.parse(event.data); const parsed = parseServerMessage(obj); if (parsed?.kind === "state") { callbacks.onState(parsed.data); } else if (parsed?.kind === "chat") { callbacks.onChat(parsed.data); } } catch { // ignore malformed JSON } };
ws.onclose = (event: CloseEvent) => { ws = null; if (!opened) { callbacks.onError("Failed to connect to room"); } else if (event.code !== 1000 && event.code !== 1001) { callbacks.onError(`Connection lost (code: ${event.code})`); } callbacks.onDisconnect(); };
ws.onerror = () => { ws?.close(); };
return (msg: ClientMessage) => { send(JSON.stringify(msg)); };}