| 21 | const CACHE_TTL = 5 * 60 * 1000; |
| 22 | |
| 23 | async function fetchLiveInfo( |
| 24 | handle: string, |
| 25 | ): Promise<{ videoId: string | null; hlsUrl: string | null }> { |
| 26 | const cached = liveInfoCache.get(handle); |
| 27 | if (cached && Date.now() - cached.ts < CACHE_TTL) { |
| 28 | return { videoId: cached.videoId, hlsUrl: cached.hlsUrl }; |
| 29 | } |
| 30 | try { |
| 31 | const res = await fetch( |
| 32 | `/api/youtube-live?channel=${encodeURIComponent(handle)}`, |
| 33 | ); |
| 34 | if (!res.ok) throw new Error("API error"); |
| 35 | const data = await res.json(); |
| 36 | const result = { videoId: data.videoId || null, hlsUrl: data.hlsUrl || null }; |
| 37 | liveInfoCache.set(handle, { ...result, ts: Date.now() }); |
| 38 | return result; |
| 39 | } catch { |
| 40 | return { videoId: null, hlsUrl: null }; |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | /** Dropdown button for the Panel header — renders channel list inline */ |
| 45 | export function LiveChannelDropdown({ |