mirror of
https://github.com/yusufipk/YTChatHub.git
synced 2026-09-11 10:56:17 +00:00
Backend: one SSE stream for messages, selection, poll and connection status; reconnect with backoff; no on-disk Innertube session cache; mock only with MOCK_CHAT=1; binds to 127.0.0.1; serves client/out so production runs on one port. Full URLs for links YouTube truncates in chat. Client: components split out, Lucide icons, stream title and status, on-air strip, search, pause, keyboard shortcuts, overlay settings dialog. Overlay themes, position, size, animation and auto-hide via URL, sized for 1080p and scaled with the source. New blueprint theme for the brand background. Removed Tailwind, better-sqlite3, zod, the timezone helpers, Cline memory bank and AGENTS.md. Added ESLint, node:test tests and CI.
30 lines
965 B
TypeScript
30 lines
965 B
TypeScript
import { BACKEND_URL } from './config';
|
|
|
|
async function call<T = unknown>(path: string, init?: RequestInit): Promise<T> {
|
|
const response = await fetch(`${BACKEND_URL}${path}`, init);
|
|
if (!response.ok) {
|
|
let message = `HTTP ${response.status}`;
|
|
try {
|
|
const body = await response.json();
|
|
if (body?.error) message = String(body.error);
|
|
} catch {
|
|
// body was not JSON
|
|
}
|
|
throw new Error(message);
|
|
}
|
|
return response.json() as Promise<T>;
|
|
}
|
|
|
|
const json = (body: unknown): RequestInit => ({
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(body)
|
|
});
|
|
|
|
export const api = {
|
|
connect: (liveId: string) => call<{ ok: true; liveId: string }>('/chat/connect', json({ liveId })),
|
|
disconnect: () => call('/chat/disconnect', { method: 'POST' }),
|
|
select: (id: string) => call('/overlay/selection', json({ id })),
|
|
clearSelection: () => call('/overlay/selection', { method: 'DELETE' })
|
|
};
|