From 50bf16cd6e2c832c5f6b6154a6dfc149c8d1da2a Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 24 Dec 2025 23:32:50 -0800 Subject: [PATCH] Update environment variable name for Cloudflare live input UID and refactor StreamPage to fetch HLS URL from API - Rename `CLOUDFLARE_STREAM_NIKIV_VIDEO_ID` to `CLOUDFLARE_LIVE_INPUT_UID` in env.d.ts - Remove hardcoded `LIVE_INPUT_UID` constant from `$username.tsx` - Add `hlsUrl` state and update it based on API response in `$username.tsx` - Modify stream playback logic to use `hlsUrl` for nikiv user - Fetch HLS URL from server-side API and update state accordingly - Update `check-hls.ts` to derive HLS URL dynamically from environment variables - Remove `CLOUDFLARE_LIVE_INPUT_UID` from `wrangler.jsonc` environment variables --- packages/web/src/env.d.ts | 2 +- packages/web/src/routes/$username.tsx | 27 +++++++++++++++--------- packages/web/src/routes/api/check-hls.ts | 18 ++++++++++++---- packages/web/wrangler.jsonc | 3 +-- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/packages/web/src/env.d.ts b/packages/web/src/env.d.ts index 96aa6664..3d36daeb 100644 --- a/packages/web/src/env.d.ts +++ b/packages/web/src/env.d.ts @@ -13,7 +13,7 @@ declare namespace Cloudflare { OPENROUTER_MODEL?: string GEMINI_API_KEY?: string FLOWGLAD_SECRET_KEY?: string - CLOUDFLARE_STREAM_NIKIV_VIDEO_ID?: string + CLOUDFLARE_LIVE_INPUT_UID?: string } } diff --git a/packages/web/src/routes/$username.tsx b/packages/web/src/routes/$username.tsx index 61f641f9..5d100cf1 100644 --- a/packages/web/src/routes/$username.tsx +++ b/packages/web/src/routes/$username.tsx @@ -22,9 +22,6 @@ export const Route = createFileRoute("/$username")({ component: StreamPage, }) -// Cloudflare Live Input UID (constant - automatically shows current live stream) -const LIVE_INPUT_UID = "bb7858eafc85de6c92963f3817477b5d" -const HLS_URL = `https://customer-xctsztqzu046isdc.cloudflarestream.com/${LIVE_INPUT_UID}/manifest/video.m3u8` const READY_PULSE_MS = 1200 // Hardcoded user for nikiv (hls_url will be updated from API) @@ -59,6 +56,7 @@ function StreamPage() { const [error, setError] = useState(null) const [playerReady, setPlayerReady] = useState(false) const [hlsLive, setHlsLive] = useState(null) + const [hlsUrl, setHlsUrl] = useState(null) const [isConnecting, setIsConnecting] = useState(false) const [nowPlaying, setNowPlaying] = useState( null, @@ -78,9 +76,12 @@ function StreamPage() { useEffect(() => { let isActive = true - // Special handling for nikiv - hardcoded stream with constant Live Input URL + // Special handling for nikiv - URL comes from API (secret) if (username === "nikiv") { - setData(makeNikivData(HLS_URL)) + // Data will be set when we get the HLS URL from the API + if (hlsUrl) { + setData(makeNikivData(hlsUrl)) + } setLoading(false) return () => { isActive = false @@ -168,9 +169,9 @@ function StreamPage() { }, [playerReady]) const stream = data?.stream ?? null - // For nikiv, always use HLS directly (no WebRTC) - const activePlayback = username === "nikiv" - ? { type: "hls" as const, url: HLS_URL } + // For nikiv, always use HLS directly (no WebRTC) - URL comes from API + const activePlayback = username === "nikiv" && hlsUrl + ? { type: "hls" as const, url: hlsUrl } : stream?.playback ?? null const isHlsPlaylistLive = (manifest: string) => { @@ -184,7 +185,7 @@ function StreamPage() { return isValidManifest && !hasEndlist && !isVod && hasSegments } - // For nikiv, use server-side API to check HLS (avoids CORS) + // For nikiv, use server-side API to check HLS (avoids CORS, gets URL from secret) useEffect(() => { if (username !== "nikiv") return @@ -197,6 +198,12 @@ function StreamPage() { const apiData = await res.json() + // Update HLS URL from API (comes from server secret) + if (apiData.hlsUrl && apiData.hlsUrl !== hlsUrl) { + setHlsUrl(apiData.hlsUrl) + setData(makeNikivData(apiData.hlsUrl)) + } + if (apiData.isLive) { // Stream is live - set connecting state if first time if (!hasConnectedOnce.current) { @@ -226,7 +233,7 @@ function StreamPage() { isActive = false clearInterval(interval) } - }, [username]) + }, [username, hlsUrl]) // For non-nikiv users, use direct HLS check useEffect(() => { diff --git a/packages/web/src/routes/api/check-hls.ts b/packages/web/src/routes/api/check-hls.ts index bc33e308..14082ab7 100644 --- a/packages/web/src/routes/api/check-hls.ts +++ b/packages/web/src/routes/api/check-hls.ts @@ -6,12 +6,22 @@ const json = (data: unknown, status = 200) => headers: { "content-type": "application/json" }, }) -// Cloudflare Live Input UID (constant - automatically shows current live stream) -const LIVE_INPUT_UID = "bb7858eafc85de6c92963f3817477b5d" -const HLS_URL = `https://customer-xctsztqzu046isdc.cloudflarestream.com/${LIVE_INPUT_UID}/manifest/video.m3u8` +// Cloudflare customer subdomain +const CLOUDFLARE_CUSTOMER_CODE = "xctsztqzu046isdc" function getHlsUrl(): string { - return HLS_URL + try { + const { getServerContext } = require("@tanstack/react-start/server") as { + getServerContext: () => { cloudflare?: { env?: Record } } | null + } + const ctx = getServerContext() + const liveInputUid = ctx?.cloudflare?.env?.CLOUDFLARE_LIVE_INPUT_UID + if (liveInputUid) { + return `https://customer-${CLOUDFLARE_CUSTOMER_CODE}.cloudflarestream.com/${liveInputUid}/manifest/video.m3u8` + } + } catch {} + // Fallback - should not happen in production + throw new Error("CLOUDFLARE_LIVE_INPUT_UID not configured") } function isHlsPlaylistLive(manifest: string): boolean { diff --git a/packages/web/wrangler.jsonc b/packages/web/wrangler.jsonc index c6b1d0ed..665c6ea6 100644 --- a/packages/web/wrangler.jsonc +++ b/packages/web/wrangler.jsonc @@ -29,8 +29,7 @@ * https://developers.cloudflare.com/workers/wrangler/configuration/#environment-variables */ "vars": { - "APP_BASE_URL": "https://linsa.io", - "CLOUDFLARE_LIVE_INPUT_UID": "bb7858eafc85de6c92963f3817477b5d" + "APP_BASE_URL": "https://linsa.io" }, /** * Note: Use secrets to store sensitive data.