Files
YTChatHub/backend/src/liveId.ts
T
yusufipek 6bcbd5400b Rewrite dashboard and overlay, single /events stream, static client served by the backend
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.
2026-09-02 22:05:58 +03:00

24 lines
823 B
TypeScript

const VIDEO_ID = /^[A-Za-z0-9_-]{11}$/;
const YOUTUBE_HOST = /(^|\.)(youtube\.com|youtube-nocookie\.com|youtu\.be)$/;
/**
* Extracts the 11-character video id from a bare id or any YouTube URL
* (watch?v=, youtu.be/, /live/, /embed/, with or without scheme). Returns '' for anything else.
*/
export function extractLiveId(input: string | undefined): string {
const trimmed = (input ?? '').trim();
if (!trimmed) return '';
if (VIDEO_ID.test(trimmed)) return trimmed;
let url: URL;
try {
url = new URL(/^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`);
} catch {
return '';
}
if (!YOUTUBE_HOST.test(url.hostname)) return '';
const candidate = url.searchParams.get('v') ?? url.pathname.split('/').filter(Boolean).pop() ?? '';
return VIDEO_ID.test(candidate) ? candidate : '';
}