@mdcrtrs/player 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +51 -0
- package/README.md +77 -0
- package/dist/index.cjs +667 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +87 -0
- package/dist/index.d.ts +87 -0
- package/dist/index.js +628 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/player.tsx","../src/hooks/use-playback-session.ts","../src/types.ts","../src/lib/api.ts","../src/hooks/use-player-events.ts","../src/hooks/use-volume.ts","../src/hooks/use-watch-progress.ts","../src/lib/session.ts","../src/lib/theme.ts"],"sourcesContent":["export { MediaCreatorsPlayer } from \"./components/player\"\nexport { usePlaybackSession } from \"./hooks/use-playback-session\"\nexport { useWatchProgress } from \"./hooks/use-watch-progress\"\nexport type {\n MediaCreatorsPlayerProps,\n PlaybackTokens,\n PlayerAsset,\n PlayerAuth,\n PlayerError,\n PlayerEvents,\n PlayerMetadata,\n PlayerTheme,\n SessionToken,\n WatchProgressRecord,\n} from \"./types\"\n","import { type FC, memo, useCallback, useEffect, useMemo, useRef } from \"react\"\nimport MuxPlayer from \"@mux/mux-player-react\"\nimport \"player.style/sutro\"\nimport { usePlaybackSession } from \"../hooks/use-playback-session\"\nimport { usePlayerEvents } from \"../hooks/use-player-events\"\nimport { useVolume } from \"../hooks/use-volume\"\nimport { useWatchProgress } from \"../hooks/use-watch-progress\"\nimport { getSessionId } from \"../lib/session\"\nimport { deriveTheme } from \"../lib/theme\"\nimport { PLATFORM } from \"../types\"\nimport type { MediaCreatorsPlayerProps } from \"../types\"\n\nfunction decodeJwtSub(token: string): string | null {\n try {\n const payloadPart = token.split(\".\")[1]\n if (!payloadPart) return null\n const padded = payloadPart + \"==\".slice((payloadPart.length % 4 || 4) - 2)\n const base64 = padded.replace(/-/g, \"+\").replace(/_/g, \"/\")\n const json = JSON.parse(atob(base64)) as { sub?: string }\n return typeof json.sub === \"string\" ? json.sub : null\n } catch {\n return null\n }\n}\n\nexport const MediaCreatorsPlayer: FC<MediaCreatorsPlayerProps> = memo(function MediaCreatorsPlayer({\n creatorId,\n asset,\n auth,\n theme,\n playerTheme,\n poster,\n metadata,\n autoResume,\n className,\n onTimeUpdate,\n onQuartile,\n onPause,\n onPlay,\n onSeeked,\n onComplete,\n onError,\n onReady,\n onResume,\n}) {\n const durationHint = asset.duration ?? 3600\n const { tokens, isLoading, error } = usePlaybackSession(creatorId, asset.playbackId, durationHint, auth?.fanToken)\n\n const colors = useMemo(() => deriveTheme(theme?.primaryColor ?? \"#1a1a1a\"), [theme?.primaryColor])\n const { initialVolume, handleVolumeChange } = useVolume()\n const playerRef = useRef<HTMLElement | null>(null)\n const seekDoneRef = useRef(false)\n\n const events = usePlayerEvents(\n { onTimeUpdate, onQuartile, onPause, onPlay, onSeeked, onComplete, onError, onReady, onResume },\n asset.assetId\n )\n\n const { resumePosition, saveProgress } = useWatchProgress(\n creatorId,\n asset.assetId,\n auth?.fanToken,\n autoResume,\n onResume\n )\n\n useEffect(() => {\n if (error) {\n onError?.({ code: \"SESSION_ERROR\", message: error.message })\n }\n }, [error, onError])\n\n useEffect(() => {\n seekDoneRef.current = false\n }, [asset.assetId])\n\n useEffect(() => {\n if (seekDoneRef.current) return\n if (resumePosition === null || !playerRef.current) return\n const el = playerRef.current as unknown as HTMLMediaElement\n if (typeof el.currentTime === \"number\") {\n el.currentTime = resumePosition\n seekDoneRef.current = true\n }\n }, [resumePosition, tokens])\n\n const handlePlayerRef = useCallback(\n (el: HTMLElement | null) => {\n playerRef.current = el\n if (el && initialVolume !== null) {\n ;(el as unknown as HTMLMediaElement).volume = initialVolume\n }\n },\n [initialVolume]\n )\n\n const viewerUserId =\n metadata?.viewerUserId ?? (auth?.fanToken ? decodeJwtSub(auth.fanToken) : null) ?? getSessionId() ?? undefined\n\n if (isLoading || !tokens) {\n return <div className={className} style={{ width: \"100%\", aspectRatio: \"16 / 9\", background: \"#111\" }} />\n }\n\n return (\n <MuxPlayer\n ref={handlePlayerRef}\n className={className}\n theme={playerTheme}\n playbackId={asset.playbackId}\n poster={poster}\n envKey={PLATFORM.dataEnvKey}\n tokens={{\n playback: tokens.video,\n thumbnail: tokens.thumbnail,\n storyboard: tokens.storyboard,\n }}\n primaryColor={colors.primary}\n secondaryColor={colors.secondary}\n accentColor={colors.accent}\n metadata={{\n video_id: asset.assetId,\n video_title: asset.title,\n video_stream_type: \"on-demand\",\n viewer_user_id: viewerUserId,\n sub_property_id: creatorId,\n player_name: metadata?.playerName ?? \"mediacreators-sdk\",\n custom_1: metadata?.custom1 ?? creatorId,\n custom_2: metadata?.custom2 ?? \"sdk\",\n }}\n playsInline\n style={{ width: \"100%\", aspectRatio: \"16 / 9\" }}\n onLoadedData={() => events.handleReady()}\n onTimeUpdate={(e) => {\n const el = e.target as HTMLMediaElement | null\n if (!el) return\n events.handleTimeUpdate(el.currentTime, el.duration)\n if (auth?.fanToken) {\n saveProgress(el.currentTime, el.duration)\n }\n }}\n onPause={(e) => {\n const el = e.target as HTMLMediaElement | null\n events.handlePause(el?.currentTime ?? 0)\n }}\n onPlay={(e) => {\n const el = e.target as HTMLMediaElement | null\n events.handlePlay(el?.currentTime ?? 0)\n }}\n onEnded={() => events.handleComplete()}\n onVolumeChange={(e) => {\n const el = e.target as HTMLMediaElement | null\n if (el && typeof el.volume === \"number\") {\n handleVolumeChange(el.volume)\n }\n }}\n onError={() => events.handleError(\"Playback error\")}\n />\n )\n})\n","import { useEffect, useRef, useState } from \"react\"\nimport { fetchPlaybackTokens, fetchSessionToken } from \"../lib/api\"\nimport type { PlaybackTokens } from \"../types\"\n\ntype RefreshFn = () => Promise<void>\n\nfunction scheduleRefresh(\n expiresIn: number,\n refreshFn: RefreshFn,\n timerRef: { current: ReturnType<typeof setTimeout> | null }\n): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current)\n }\n const refreshAtMs = expiresIn * 0.8 * 1000\n timerRef.current = setTimeout(async () => {\n try {\n await refreshFn()\n } catch (err) {\n console.error(\"[player/session] Scheduled refresh failed:\", err)\n }\n }, refreshAtMs)\n}\n\nfunction cancelRefresh(timerRef: { current: ReturnType<typeof setTimeout> | null }): void {\n if (timerRef.current !== null) {\n clearTimeout(timerRef.current)\n timerRef.current = null\n }\n}\n\nexport interface UsePlaybackSessionResult {\n tokens: PlaybackTokens | null\n isLoading: boolean\n error: Error | null\n}\n\nexport function usePlaybackSession(\n creatorId: string,\n playbackId: string,\n duration: number,\n fanToken?: string\n): UsePlaybackSessionResult {\n const [tokens, setTokens] = useState<PlaybackTokens | null>(null)\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<Error | null>(null)\n\n const sessionTokenRef = useRef<string | null>(null)\n const sessionExpiresAtRef = useRef<Date | null>(null)\n const tokensRef = useRef<PlaybackTokens | null>(null)\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n const visibilityHandlerRef = useRef<(() => void) | null>(null)\n\n useEffect(() => {\n let cancelled = false\n tokensRef.current = null\n setTokens(null)\n setIsLoading(true)\n setError(null)\n\n async function refresh(): Promise<void> {\n if (!sessionTokenRef.current || !sessionExpiresAtRef.current) return\n\n const now = Date.now()\n const sessionExpiry = sessionExpiresAtRef.current.getTime()\n let sessionToken = sessionTokenRef.current\n\n if (sessionExpiry - now < 60_000) {\n const newSession = await fetchSessionToken(creatorId, fanToken)\n sessionToken = newSession.token\n sessionTokenRef.current = newSession.token\n sessionExpiresAtRef.current = new Date(newSession.expiresAt)\n }\n\n const nextTokens = await fetchPlaybackTokens(creatorId, playbackId, duration, sessionToken)\n if (cancelled) return\n tokensRef.current = nextTokens\n setTokens(nextTokens)\n scheduleRefresh(nextTokens.expiresIn, refresh, timerRef)\n }\n\n async function initialize(): Promise<void> {\n try {\n const session = await fetchSessionToken(creatorId, fanToken)\n if (cancelled) return\n\n sessionTokenRef.current = session.token\n sessionExpiresAtRef.current = new Date(session.expiresAt)\n\n const initialTokens = await fetchPlaybackTokens(creatorId, playbackId, duration, session.token)\n if (cancelled) return\n\n tokensRef.current = initialTokens\n setTokens(initialTokens)\n scheduleRefresh(initialTokens.expiresIn, refresh, timerRef)\n\n visibilityHandlerRef.current = () => {\n if (document.visibilityState !== \"visible\") return\n const current = tokensRef.current\n if (!sessionTokenRef.current || !sessionExpiresAtRef.current || !current) return\n\n const now = Date.now()\n const sessionExpiry = sessionExpiresAtRef.current.getTime()\n const approxTokenExpiry = now + current.expiresIn * 1000\n const sessionExpired = sessionExpiry <= now\n const tokenNearExpiry = approxTokenExpiry - now < current.expiresIn * 0.2 * 1000\n\n if (sessionExpired || tokenNearExpiry) {\n cancelRefresh(timerRef)\n refresh().catch((err: Error) => {\n if (!cancelled) setError(err)\n })\n }\n }\n document.addEventListener(\"visibilitychange\", visibilityHandlerRef.current)\n } catch (err) {\n if (!cancelled) {\n setError(err instanceof Error ? err : new Error(String(err)))\n setTokens(null)\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n\n void initialize()\n\n return () => {\n cancelled = true\n cancelRefresh(timerRef)\n if (visibilityHandlerRef.current) {\n document.removeEventListener(\"visibilitychange\", visibilityHandlerRef.current)\n visibilityHandlerRef.current = null\n }\n sessionTokenRef.current = null\n sessionExpiresAtRef.current = null\n }\n }, [creatorId, playbackId, duration, fanToken])\n\n return { tokens, isLoading, error }\n}\n","export const PLATFORM = {\n apiBase: \"https://api.mediacreators.io\",\n playbackBase: \"https://playback.mediacreators.io\",\n dataEnvKey: \"fhri2484lqcn6gk89r013ckmb\",\n} as const\n\nexport interface PlayerAsset {\n assetId: string\n playbackId: string\n title: string\n /** Optional duration hint (seconds) for token TTL; defaults to 3600 */\n duration?: number\n}\n\nexport interface PlayerAuth {\n fanToken: string\n}\n\nexport interface PlayerTheme {\n primaryColor: string\n}\n\nexport interface PlayerMetadata {\n playerName?: string\n custom1?: string\n custom2?: string\n viewerUserId?: string\n}\n\nexport interface PlayerError {\n code: string\n message: string\n}\n\nexport interface PlayerEvents {\n onTimeUpdate?: (currentTime: number, duration: number, pct: number) => void\n onQuartile?: (quartile: 25 | 50 | 75 | 100) => void\n onPause?: (currentTime: number) => void\n onPlay?: (currentTime: number) => void\n onSeeked?: (from: number, to: number) => void\n onComplete?: () => void\n onError?: (error: PlayerError) => void\n onReady?: () => void\n onResume?: (position: number) => void\n}\n\nexport interface MediaCreatorsPlayerProps extends PlayerEvents {\n creatorId: string\n asset: PlayerAsset\n auth?: PlayerAuth\n theme?: PlayerTheme\n /** Player chrome theme name. Default: Sutro. */\n playerTheme?: string\n /** Poster / thumbnail shown before playback. */\n poster?: string\n metadata?: PlayerMetadata\n autoResume?: boolean\n className?: string\n}\n\nexport interface PlaybackTokens {\n video: string\n thumbnail: string\n storyboard: string\n expiresIn: number\n}\n\nexport interface SessionToken {\n token: string\n expiresAt: string\n}\n\nexport interface WatchProgressRecord {\n asset_id: string\n position: number\n duration: number\n completed: boolean\n progress_pct: number\n updated_at: string\n title?: string\n thumbnail_url?: string | null\n}\n","import { PLATFORM } from \"../types\"\nimport type { PlaybackTokens, SessionToken, WatchProgressRecord } from \"../types\"\n\nclass ApiError extends Error {\n constructor(\n message: string,\n public readonly status: number\n ) {\n super(message)\n this.name = \"ApiError\"\n }\n}\n\nasync function fetchWithRetry(url: string, init?: RequestInit, maxRetries = 3): Promise<Response> {\n let lastError: Error = new Error(\"Unknown fetch error\")\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n const res = await fetch(url, init)\n if (res.ok || res.status < 500) {\n return res\n }\n lastError = new ApiError(`HTTP ${res.status}`, res.status)\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err))\n }\n if (attempt < maxRetries - 1) {\n await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 500))\n }\n }\n throw lastError\n}\n\nexport async function fetchSessionToken(creatorId: string, fanToken?: string): Promise<SessionToken> {\n const headers: Record<string, string> = {}\n if (fanToken) {\n headers[\"X-Fan-Token\"] = fanToken\n }\n\n const url = `${PLATFORM.apiBase}/v1/mos/${encodeURIComponent(creatorId)}/playback-session`\n const res = await fetchWithRetry(url, { method: \"POST\", headers })\n\n if (!res.ok) {\n throw new ApiError(`Failed to fetch session token: ${res.status}`, res.status)\n }\n\n const json = (await res.json()) as { success: true; token: string; expires_at: string }\n return { token: json.token, expiresAt: json.expires_at }\n}\n\nexport async function fetchPlaybackTokens(\n creatorId: string,\n playbackId: string,\n duration: number,\n sessionToken: string\n): Promise<PlaybackTokens> {\n const params = new URLSearchParams({ duration: String(duration) })\n const url = `${PLATFORM.playbackBase}/v1/${encodeURIComponent(creatorId)}/tokens/${encodeURIComponent(playbackId)}?${params}`\n\n const res = await fetchWithRetry(url, {\n headers: { Authorization: `Bearer ${sessionToken}` },\n })\n\n if (res.status === 401 || res.status === 403) {\n throw new ApiError(\"Session token invalid or expired\", res.status)\n }\n if (!res.ok) {\n throw new ApiError(`Failed to fetch playback tokens: ${res.status}`, res.status)\n }\n\n const json = (await res.json()) as {\n success: true\n tokens: { video: string; thumbnail: string; storyboard: string }\n expires_in: number\n }\n\n return {\n video: json.tokens.video,\n thumbnail: json.tokens.thumbnail,\n storyboard: json.tokens.storyboard,\n expiresIn: json.expires_in,\n }\n}\n\nexport type SaveProgressResult = { ok: true } | { ok: false; status: number }\n\nexport async function saveWatchProgress(\n creatorId: string,\n assetId: string,\n position: number,\n duration: number,\n fanToken: string,\n completed?: boolean\n): Promise<SaveProgressResult> {\n const url = `${PLATFORM.apiBase}/v1/mos/${encodeURIComponent(creatorId)}/watch-progress/${encodeURIComponent(assetId)}`\n try {\n const res = await fetch(url, {\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Fan-Token\": fanToken,\n },\n body: JSON.stringify({ position, duration, completed: completed ?? false }),\n keepalive: true,\n })\n if (!res.ok) return { ok: false, status: res.status }\n return { ok: true }\n } catch {\n return { ok: false, status: 0 }\n }\n}\n\nexport async function getWatchProgress(\n creatorId: string,\n assetId: string,\n fanToken: string\n): Promise<WatchProgressRecord | null> {\n const url = `${PLATFORM.apiBase}/v1/mos/${encodeURIComponent(creatorId)}/watch-progress/${encodeURIComponent(assetId)}`\n try {\n const res = await fetch(url, {\n headers: { \"X-Fan-Token\": fanToken },\n })\n if (res.status === 401 || res.status === 404) return null\n if (!res.ok) return null\n const json = (await res.json()) as { success: true; data: WatchProgressRecord }\n return json.data\n } catch {\n return null\n }\n}\n","import { useEffect, useRef } from \"react\"\nimport type { PlayerEvents } from \"../types\"\n\nconst QUARTILES = [25, 50, 75, 100] as const\n\nexport function usePlayerEvents(events: PlayerEvents, assetId: string) {\n const firedQuartiles = useRef(new Set<number>())\n const prevTime = useRef(0)\n const eventsRef = useRef(events)\n eventsRef.current = events\n\n useEffect(() => {\n firedQuartiles.current = new Set()\n prevTime.current = 0\n }, [assetId])\n\n return {\n handleTimeUpdate: (currentTime: number, duration: number) => {\n if (!duration || duration <= 0) return\n const pct = (currentTime / duration) * 100\n eventsRef.current.onTimeUpdate?.(currentTime, duration, pct)\n\n for (const q of QUARTILES) {\n if (pct >= q && !firedQuartiles.current.has(q)) {\n firedQuartiles.current.add(q)\n eventsRef.current.onQuartile?.(q)\n }\n }\n\n const delta = Math.abs(currentTime - prevTime.current)\n // Heuristic seek: jump > 2s without being a normal play tick\n if (delta > 2 && prevTime.current > 0) {\n eventsRef.current.onSeeked?.(prevTime.current, currentTime)\n }\n prevTime.current = currentTime\n },\n handlePause: (currentTime: number) => {\n eventsRef.current.onPause?.(currentTime)\n },\n handlePlay: (currentTime: number) => {\n eventsRef.current.onPlay?.(currentTime)\n },\n handleComplete: () => {\n if (!firedQuartiles.current.has(100)) {\n firedQuartiles.current.add(100)\n eventsRef.current.onQuartile?.(100)\n }\n eventsRef.current.onComplete?.()\n },\n handleReady: () => {\n eventsRef.current.onReady?.()\n },\n handleError: (message: string) => {\n eventsRef.current.onError?.({ code: \"PLAYER_ERROR\", message })\n },\n }\n}\n","import { useCallback, useRef } from \"react\"\n\nconst STORAGE_KEY = \"mc-player-volume\"\n\nfunction getStoredVolume(): number | null {\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY)\n if (raw === null) return null\n const parsed = parseFloat(raw)\n if (Number.isNaN(parsed) || parsed < 0 || parsed > 1) return null\n return parsed\n } catch {\n return null\n }\n}\n\nfunction saveVolume(volume: number): void {\n try {\n window.localStorage.setItem(STORAGE_KEY, String(volume))\n } catch {\n // localStorage may be blocked (iframe / private mode)\n }\n}\n\nexport function useVolume(): {\n initialVolume: number | null\n handleVolumeChange: (volume: number) => void\n} {\n const initialVolume = useRef(getStoredVolume())\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n\n const handleVolumeChange = useCallback((volume: number) => {\n if (debounceTimer.current !== null) clearTimeout(debounceTimer.current)\n debounceTimer.current = setTimeout(() => saveVolume(volume), 500)\n }, [])\n\n return { initialVolume: initialVolume.current, handleVolumeChange }\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\"\nimport { getWatchProgress, saveWatchProgress } from \"../lib/api\"\n\nconst SAVE_DEBOUNCE_MS = 10_000\n\nexport interface UseWatchProgressResult {\n resumePosition: number | null\n saveProgress: (position: number, duration: number) => void\n authExpired: boolean\n}\n\nexport function useWatchProgress(\n creatorId: string,\n assetId: string,\n fanToken: string | undefined,\n autoResume: boolean | undefined,\n onResume?: (position: number) => void,\n): UseWatchProgressResult {\n const [resumePosition, setResumePosition] = useState<number | null>(null)\n const [authExpired, setAuthExpired] = useState(false)\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const onResumeRef = useRef(onResume)\n onResumeRef.current = onResume\n const stoppedRef = useRef(false)\n\n useEffect(() => {\n stoppedRef.current = false\n setAuthExpired(false)\n setResumePosition(null)\n\n if (!fanToken || !autoResume) return\n\n let cancelled = false\n void getWatchProgress(creatorId, assetId, fanToken).then((record) => {\n if (cancelled || !record || record.completed) return\n if (record.position > 5) {\n setResumePosition(record.position)\n onResumeRef.current?.(record.position)\n }\n })\n\n return () => {\n cancelled = true\n if (debounceTimer.current !== null) {\n clearTimeout(debounceTimer.current)\n debounceTimer.current = null\n }\n }\n }, [creatorId, assetId, fanToken, autoResume])\n\n const saveProgress = useCallback(\n (position: number, duration: number) => {\n if (!fanToken || stoppedRef.current) return\n\n if (debounceTimer.current !== null) clearTimeout(debounceTimer.current)\n debounceTimer.current = setTimeout(() => {\n const completed = duration > 0 && position / duration >= 0.95\n void saveWatchProgress(creatorId, assetId, position, duration, fanToken, completed).then((result) => {\n if (!result.ok && result.status === 401) {\n stoppedRef.current = true\n setAuthExpired(true)\n }\n })\n }, SAVE_DEBOUNCE_MS)\n },\n [creatorId, assetId, fanToken],\n )\n\n return { resumePosition, saveProgress, authExpired }\n}\n","const SESSION_STORAGE_KEY = \"mc-player-session\"\n\n/** Stable per-tab anonymous session id for analytics viewer tracking. */\nexport function getSessionId(): string | null {\n try {\n const existing = window.sessionStorage.getItem(SESSION_STORAGE_KEY)\n if (existing) return existing\n\n const generated = crypto.randomUUID()\n window.sessionStorage.setItem(SESSION_STORAGE_KEY, generated)\n return generated\n } catch {\n return null\n }\n}\n","export interface HSL {\n h: number\n s: number\n l: number\n}\n\nexport interface DerivedColors {\n primary: string\n secondary: string\n accent: string\n}\n\nfunction normalizeHex(hex: string): string {\n const sanitized = hex.trim().replace(/^#/, \"\")\n if (!/^[0-9a-fA-F]{3}$|^[0-9a-fA-F]{6}$/.test(sanitized)) {\n return \"#1a1a1a\"\n }\n if (sanitized.length === 3) {\n return `#${sanitized\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")}`\n }\n return `#${sanitized}`\n}\n\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } {\n const full = normalizeHex(hex).slice(1)\n return {\n r: parseInt(full.slice(0, 2), 16),\n g: parseInt(full.slice(2, 4), 16),\n b: parseInt(full.slice(4, 6), 16),\n }\n}\n\nexport function relativeLuminance(hex: string): number {\n const { r, g, b } = hexToRgb(hex)\n const linear = [r, g, b].map((channel) => {\n const s = channel / 255\n return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4\n })\n return 0.2126 * linear[0]! + 0.7152 * linear[1]! + 0.0722 * linear[2]!\n}\n\nexport function hexToHSL(hex: string): HSL {\n const { r, g, b } = hexToRgb(hex)\n const rNorm = r / 255\n const gNorm = g / 255\n const bNorm = b / 255\n\n const max = Math.max(rNorm, gNorm, bNorm)\n const min = Math.min(rNorm, gNorm, bNorm)\n const delta = max - min\n\n let h = 0\n if (delta !== 0) {\n if (max === rNorm) {\n h = ((gNorm - bNorm) / delta) % 6\n } else if (max === gNorm) {\n h = (bNorm - rNorm) / delta + 2\n } else {\n h = (rNorm - gNorm) / delta + 4\n }\n h = Math.round(h * 60)\n if (h < 0) h += 360\n }\n\n const l = (max + min) / 2\n const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1))\n\n return {\n h,\n s: Math.round(s * 100),\n l: Math.round(l * 100),\n }\n}\n\nexport function hslToHex({ h, s, l }: HSL): string {\n const sNorm = s / 100\n const lNorm = l / 100\n\n const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1))\n const m = lNorm - c / 2\n\n let r = 0\n let g = 0\n let b = 0\n\n if (h < 60) {\n ;[r, g, b] = [c, x, 0]\n } else if (h < 120) {\n ;[r, g, b] = [x, c, 0]\n } else if (h < 180) {\n ;[r, g, b] = [0, c, x]\n } else if (h < 240) {\n ;[r, g, b] = [0, x, c]\n } else if (h < 300) {\n ;[r, g, b] = [x, 0, c]\n } else {\n ;[r, g, b] = [c, 0, x]\n }\n\n const toHex = (v: number) =>\n Math.round((v + m) * 255)\n .toString(16)\n .padStart(2, \"0\")\n\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/** Maps a creator brand color to player theme tokens with readable contrast. */\nexport function deriveColors(primaryHex: string): DerivedColors {\n const brand = normalizeHex(primaryHex)\n const hsl = hexToHSL(brand)\n const lum = relativeLuminance(brand)\n\n const accentL = clamp(hsl.l < 42 ? 50 : hsl.l > 78 ? 70 : hsl.l, 38, 72)\n const accent = hslToHex({\n h: hsl.h,\n s: clamp(hsl.s, 0, 100),\n l: accentL,\n })\n\n let primary: string\n if (lum >= 0.72) {\n primary = \"#1a1a1a\"\n } else if (lum >= 0.58 && hsl.s < 22) {\n primary = brand\n } else {\n primary = \"#ffffff\"\n }\n\n const secondary = lum >= 0.72 ? \"rgba(0, 0, 0, 0.1)\" : \"rgba(255, 255, 255, 0.14)\"\n\n return { primary, secondary, accent }\n}\n\nexport function deriveTheme(primaryColor: string): DerivedColors {\n return deriveColors(primaryColor)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAuE;AACvE,8BAAsB;AACtB,mBAAO;;;ACFP,mBAA4C;;;ACArC,IAAM,WAAW;AAAA,EACtB,SAAS;AAAA,EACT,cAAc;AAAA,EACd,YAAY;AACd;;;ACDA,IAAM,WAAN,cAAuB,MAAM;AAAA,EAC3B,YACE,SACgB,QAChB;AACA,UAAM,OAAO;AAFG;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,eAAe,eAAe,KAAa,MAAoB,aAAa,GAAsB;AAChG,MAAI,YAAmB,IAAI,MAAM,qBAAqB;AACtD,WAAS,UAAU,GAAG,UAAU,YAAY,WAAW;AACrD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,KAAK,IAAI;AACjC,UAAI,IAAI,MAAM,IAAI,SAAS,KAAK;AAC9B,eAAO;AAAA,MACT;AACA,kBAAY,IAAI,SAAS,QAAQ,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,IAC3D,SAAS,KAAK;AACZ,kBAAY,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAAA,IAChE;AACA,QAAI,UAAU,aAAa,GAAG;AAC5B,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,UAAU,GAAG,CAAC;AAAA,IACxE;AAAA,EACF;AACA,QAAM;AACR;AAEA,eAAsB,kBAAkB,WAAmB,UAA0C;AACnG,QAAM,UAAkC,CAAC;AACzC,MAAI,UAAU;AACZ,YAAQ,aAAa,IAAI;AAAA,EAC3B;AAEA,QAAM,MAAM,GAAG,SAAS,OAAO,WAAW,mBAAmB,SAAS,CAAC;AACvE,QAAM,MAAM,MAAM,eAAe,KAAK,EAAE,QAAQ,QAAQ,QAAQ,CAAC;AAEjE,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,SAAS,kCAAkC,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,EAC/E;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,SAAO,EAAE,OAAO,KAAK,OAAO,WAAW,KAAK,WAAW;AACzD;AAEA,eAAsB,oBACpB,WACA,YACA,UACA,cACyB;AACzB,QAAM,SAAS,IAAI,gBAAgB,EAAE,UAAU,OAAO,QAAQ,EAAE,CAAC;AACjE,QAAM,MAAM,GAAG,SAAS,YAAY,OAAO,mBAAmB,SAAS,CAAC,WAAW,mBAAmB,UAAU,CAAC,IAAI,MAAM;AAE3H,QAAM,MAAM,MAAM,eAAe,KAAK;AAAA,IACpC,SAAS,EAAE,eAAe,UAAU,YAAY,GAAG;AAAA,EACrD,CAAC;AAED,MAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KAAK;AAC5C,UAAM,IAAI,SAAS,oCAAoC,IAAI,MAAM;AAAA,EACnE;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,SAAS,oCAAoC,IAAI,MAAM,IAAI,IAAI,MAAM;AAAA,EACjF;AAEA,QAAM,OAAQ,MAAM,IAAI,KAAK;AAM7B,SAAO;AAAA,IACL,OAAO,KAAK,OAAO;AAAA,IACnB,WAAW,KAAK,OAAO;AAAA,IACvB,YAAY,KAAK,OAAO;AAAA,IACxB,WAAW,KAAK;AAAA,EAClB;AACF;AAIA,eAAsB,kBACpB,WACA,SACA,UACA,UACA,UACA,WAC6B;AAC7B,QAAM,MAAM,GAAG,SAAS,OAAO,WAAW,mBAAmB,SAAS,CAAC,mBAAmB,mBAAmB,OAAO,CAAC;AACrH,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe;AAAA,MACjB;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,UAAU,WAAW,gCAAa,MAAM,CAAC;AAAA,MAC1E,WAAW;AAAA,IACb,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO,EAAE,IAAI,OAAO,QAAQ,IAAI,OAAO;AACpD,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAQ;AACN,WAAO,EAAE,IAAI,OAAO,QAAQ,EAAE;AAAA,EAChC;AACF;AAEA,eAAsB,iBACpB,WACA,SACA,UACqC;AACrC,QAAM,MAAM,GAAG,SAAS,OAAO,WAAW,mBAAmB,SAAS,CAAC,mBAAmB,mBAAmB,OAAO,CAAC;AACrH,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B,SAAS,EAAE,eAAe,SAAS;AAAA,IACrC,CAAC;AACD,QAAI,IAAI,WAAW,OAAO,IAAI,WAAW,IAAK,QAAO;AACrD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO,KAAK;AAAA,EACd,SAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AF1HA,SAAS,gBACP,WACA,WACA,UACM;AACN,MAAI,SAAS,YAAY,MAAM;AAC7B,iBAAa,SAAS,OAAO;AAAA,EAC/B;AACA,QAAM,cAAc,YAAY,MAAM;AACtC,WAAS,UAAU,WAAW,YAAY;AACxC,QAAI;AACF,YAAM,UAAU;AAAA,IAClB,SAAS,KAAK;AACZ,cAAQ,MAAM,8CAA8C,GAAG;AAAA,IACjE;AAAA,EACF,GAAG,WAAW;AAChB;AAEA,SAAS,cAAc,UAAmE;AACxF,MAAI,SAAS,YAAY,MAAM;AAC7B,iBAAa,SAAS,OAAO;AAC7B,aAAS,UAAU;AAAA,EACrB;AACF;AAQO,SAAS,mBACd,WACA,YACA,UACA,UAC0B;AAC1B,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAgC,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,IAAI;AAC/C,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAuB,IAAI;AAErD,QAAM,sBAAkB,qBAAsB,IAAI;AAClD,QAAM,0BAAsB,qBAAoB,IAAI;AACpD,QAAM,gBAAY,qBAA8B,IAAI;AACpD,QAAM,eAAW,qBAA6C,IAAI;AAClE,QAAM,2BAAuB,qBAA4B,IAAI;AAE7D,8BAAU,MAAM;AACd,QAAI,YAAY;AAChB,cAAU,UAAU;AACpB,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,aAAS,IAAI;AAEb,mBAAe,UAAyB;AACtC,UAAI,CAAC,gBAAgB,WAAW,CAAC,oBAAoB,QAAS;AAE9D,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,gBAAgB,oBAAoB,QAAQ,QAAQ;AAC1D,UAAI,eAAe,gBAAgB;AAEnC,UAAI,gBAAgB,MAAM,KAAQ;AAChC,cAAM,aAAa,MAAM,kBAAkB,WAAW,QAAQ;AAC9D,uBAAe,WAAW;AAC1B,wBAAgB,UAAU,WAAW;AACrC,4BAAoB,UAAU,IAAI,KAAK,WAAW,SAAS;AAAA,MAC7D;AAEA,YAAM,aAAa,MAAM,oBAAoB,WAAW,YAAY,UAAU,YAAY;AAC1F,UAAI,UAAW;AACf,gBAAU,UAAU;AACpB,gBAAU,UAAU;AACpB,sBAAgB,WAAW,WAAW,SAAS,QAAQ;AAAA,IACzD;AAEA,mBAAe,aAA4B;AACzC,UAAI;AACF,cAAM,UAAU,MAAM,kBAAkB,WAAW,QAAQ;AAC3D,YAAI,UAAW;AAEf,wBAAgB,UAAU,QAAQ;AAClC,4BAAoB,UAAU,IAAI,KAAK,QAAQ,SAAS;AAExD,cAAM,gBAAgB,MAAM,oBAAoB,WAAW,YAAY,UAAU,QAAQ,KAAK;AAC9F,YAAI,UAAW;AAEf,kBAAU,UAAU;AACpB,kBAAU,aAAa;AACvB,wBAAgB,cAAc,WAAW,SAAS,QAAQ;AAE1D,6BAAqB,UAAU,MAAM;AACnC,cAAI,SAAS,oBAAoB,UAAW;AAC5C,gBAAM,UAAU,UAAU;AAC1B,cAAI,CAAC,gBAAgB,WAAW,CAAC,oBAAoB,WAAW,CAAC,QAAS;AAE1E,gBAAM,MAAM,KAAK,IAAI;AACrB,gBAAM,gBAAgB,oBAAoB,QAAQ,QAAQ;AAC1D,gBAAM,oBAAoB,MAAM,QAAQ,YAAY;AACpD,gBAAM,iBAAiB,iBAAiB;AACxC,gBAAM,kBAAkB,oBAAoB,MAAM,QAAQ,YAAY,MAAM;AAE5E,cAAI,kBAAkB,iBAAiB;AACrC,0BAAc,QAAQ;AACtB,oBAAQ,EAAE,MAAM,CAAC,QAAe;AAC9B,kBAAI,CAAC,UAAW,UAAS,GAAG;AAAA,YAC9B,CAAC;AAAA,UACH;AAAA,QACF;AACA,iBAAS,iBAAiB,oBAAoB,qBAAqB,OAAO;AAAA,MAC5E,SAAS,KAAK;AACZ,YAAI,CAAC,WAAW;AACd,mBAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAC5D,oBAAU,IAAI;AAAA,QAChB;AAAA,MACF,UAAE;AACA,YAAI,CAAC,UAAW,cAAa,KAAK;AAAA,MACpC;AAAA,IACF;AAEA,SAAK,WAAW;AAEhB,WAAO,MAAM;AACX,kBAAY;AACZ,oBAAc,QAAQ;AACtB,UAAI,qBAAqB,SAAS;AAChC,iBAAS,oBAAoB,oBAAoB,qBAAqB,OAAO;AAC7E,6BAAqB,UAAU;AAAA,MACjC;AACA,sBAAgB,UAAU;AAC1B,0BAAoB,UAAU;AAAA,IAChC;AAAA,EACF,GAAG,CAAC,WAAW,YAAY,UAAU,QAAQ,CAAC;AAE9C,SAAO,EAAE,QAAQ,WAAW,MAAM;AACpC;;;AG5IA,IAAAC,gBAAkC;AAGlC,IAAM,YAAY,CAAC,IAAI,IAAI,IAAI,GAAG;AAE3B,SAAS,gBAAgB,QAAsB,SAAiB;AACrE,QAAM,qBAAiB,sBAAO,oBAAI,IAAY,CAAC;AAC/C,QAAM,eAAW,sBAAO,CAAC;AACzB,QAAM,gBAAY,sBAAO,MAAM;AAC/B,YAAU,UAAU;AAEpB,+BAAU,MAAM;AACd,mBAAe,UAAU,oBAAI,IAAI;AACjC,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,OAAO,CAAC;AAEZ,SAAO;AAAA,IACL,kBAAkB,CAAC,aAAqB,aAAqB;AAjBjE;AAkBM,UAAI,CAAC,YAAY,YAAY,EAAG;AAChC,YAAM,MAAO,cAAc,WAAY;AACvC,4BAAU,SAAQ,iBAAlB,4BAAiC,aAAa,UAAU;AAExD,iBAAW,KAAK,WAAW;AACzB,YAAI,OAAO,KAAK,CAAC,eAAe,QAAQ,IAAI,CAAC,GAAG;AAC9C,yBAAe,QAAQ,IAAI,CAAC;AAC5B,gCAAU,SAAQ,eAAlB,4BAA+B;AAAA,QACjC;AAAA,MACF;AAEA,YAAM,QAAQ,KAAK,IAAI,cAAc,SAAS,OAAO;AAErD,UAAI,QAAQ,KAAK,SAAS,UAAU,GAAG;AACrC,8BAAU,SAAQ,aAAlB,4BAA6B,SAAS,SAAS;AAAA,MACjD;AACA,eAAS,UAAU;AAAA,IACrB;AAAA,IACA,aAAa,CAAC,gBAAwB;AApC1C;AAqCM,4BAAU,SAAQ,YAAlB,4BAA4B;AAAA,IAC9B;AAAA,IACA,YAAY,CAAC,gBAAwB;AAvCzC;AAwCM,4BAAU,SAAQ,WAAlB,4BAA2B;AAAA,IAC7B;AAAA,IACA,gBAAgB,MAAM;AA1C1B;AA2CM,UAAI,CAAC,eAAe,QAAQ,IAAI,GAAG,GAAG;AACpC,uBAAe,QAAQ,IAAI,GAAG;AAC9B,8BAAU,SAAQ,eAAlB,4BAA+B;AAAA,MACjC;AACA,4BAAU,SAAQ,eAAlB;AAAA,IACF;AAAA,IACA,aAAa,MAAM;AAjDvB;AAkDM,4BAAU,SAAQ,YAAlB;AAAA,IACF;AAAA,IACA,aAAa,CAAC,YAAoB;AApDtC;AAqDM,4BAAU,SAAQ,YAAlB,4BAA4B,EAAE,MAAM,gBAAgB,QAAQ;AAAA,IAC9D;AAAA,EACF;AACF;;;ACxDA,IAAAC,gBAAoC;AAEpC,IAAM,cAAc;AAEpB,SAAS,kBAAiC;AACxC,MAAI;AACF,UAAM,MAAM,OAAO,aAAa,QAAQ,WAAW;AACnD,QAAI,QAAQ,KAAM,QAAO;AACzB,UAAM,SAAS,WAAW,GAAG;AAC7B,QAAI,OAAO,MAAM,MAAM,KAAK,SAAS,KAAK,SAAS,EAAG,QAAO;AAC7D,WAAO;AAAA,EACT,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,WAAW,QAAsB;AACxC,MAAI;AACF,WAAO,aAAa,QAAQ,aAAa,OAAO,MAAM,CAAC;AAAA,EACzD,SAAQ;AAAA,EAER;AACF;AAEO,SAAS,YAGd;AACA,QAAM,oBAAgB,sBAAO,gBAAgB,CAAC;AAC9C,QAAM,oBAAgB,sBAA6C,IAAI;AAEvE,QAAM,yBAAqB,2BAAY,CAAC,WAAmB;AACzD,QAAI,cAAc,YAAY,KAAM,cAAa,cAAc,OAAO;AACtE,kBAAc,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,GAAG;AAAA,EAClE,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,eAAe,cAAc,SAAS,mBAAmB;AACpE;;;ACrCA,IAAAC,gBAAyD;AAGzD,IAAM,mBAAmB;AAQlB,SAAS,iBACd,WACA,SACA,UACA,YACA,UACwB;AACxB,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,wBAAwB,IAAI;AACxE,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AACpD,QAAM,oBAAgB,sBAA6C,IAAI;AACvE,QAAM,kBAAc,sBAAO,QAAQ;AACnC,cAAY,UAAU;AACtB,QAAM,iBAAa,sBAAO,KAAK;AAE/B,+BAAU,MAAM;AACd,eAAW,UAAU;AACrB,mBAAe,KAAK;AACpB,sBAAkB,IAAI;AAEtB,QAAI,CAAC,YAAY,CAAC,WAAY;AAE9B,QAAI,YAAY;AAChB,SAAK,iBAAiB,WAAW,SAAS,QAAQ,EAAE,KAAK,CAAC,WAAW;AAjCzE;AAkCM,UAAI,aAAa,CAAC,UAAU,OAAO,UAAW;AAC9C,UAAI,OAAO,WAAW,GAAG;AACvB,0BAAkB,OAAO,QAAQ;AACjC,0BAAY,YAAZ,qCAAsB,OAAO;AAAA,MAC/B;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,kBAAY;AACZ,UAAI,cAAc,YAAY,MAAM;AAClC,qBAAa,cAAc,OAAO;AAClC,sBAAc,UAAU;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,SAAS,UAAU,UAAU,CAAC;AAE7C,QAAM,mBAAe;AAAA,IACnB,CAAC,UAAkB,aAAqB;AACtC,UAAI,CAAC,YAAY,WAAW,QAAS;AAErC,UAAI,cAAc,YAAY,KAAM,cAAa,cAAc,OAAO;AACtE,oBAAc,UAAU,WAAW,MAAM;AACvC,cAAM,YAAY,WAAW,KAAK,WAAW,YAAY;AACzD,aAAK,kBAAkB,WAAW,SAAS,UAAU,UAAU,UAAU,SAAS,EAAE,KAAK,CAAC,WAAW;AACnG,cAAI,CAAC,OAAO,MAAM,OAAO,WAAW,KAAK;AACvC,uBAAW,UAAU;AACrB,2BAAe,IAAI;AAAA,UACrB;AAAA,QACF,CAAC;AAAA,MACH,GAAG,gBAAgB;AAAA,IACrB;AAAA,IACA,CAAC,WAAW,SAAS,QAAQ;AAAA,EAC/B;AAEA,SAAO,EAAE,gBAAgB,cAAc,YAAY;AACrD;;;ACrEA,IAAM,sBAAsB;AAGrB,SAAS,eAA8B;AAC5C,MAAI;AACF,UAAM,WAAW,OAAO,eAAe,QAAQ,mBAAmB;AAClE,QAAI,SAAU,QAAO;AAErB,UAAM,YAAY,OAAO,WAAW;AACpC,WAAO,eAAe,QAAQ,qBAAqB,SAAS;AAC5D,WAAO;AAAA,EACT,SAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACFA,SAAS,aAAa,KAAqB;AACzC,QAAM,YAAY,IAAI,KAAK,EAAE,QAAQ,MAAM,EAAE;AAC7C,MAAI,CAAC,oCAAoC,KAAK,SAAS,GAAG;AACxD,WAAO;AAAA,EACT;AACA,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,IAAI,UACR,MAAM,EAAE,EACR,IAAI,CAAC,MAAM,IAAI,CAAC,EAChB,KAAK,EAAE,CAAC;AAAA,EACb;AACA,SAAO,IAAI,SAAS;AACtB;AAEA,SAAS,SAAS,KAAkD;AAClE,QAAM,OAAO,aAAa,GAAG,EAAE,MAAM,CAAC;AACtC,SAAO;AAAA,IACL,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAChC,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAChC,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EAClC;AACF;AAEO,SAAS,kBAAkB,KAAqB;AACrD,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,GAAG;AAChC,QAAM,SAAS,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,YAAY;AACxC,UAAM,IAAI,UAAU;AACpB,WAAO,KAAK,UAAU,IAAI,UAAU,IAAI,SAAS,UAAU;AAAA,EAC7D,CAAC;AACD,SAAO,SAAS,OAAO,CAAC,IAAK,SAAS,OAAO,CAAC,IAAK,SAAS,OAAO,CAAC;AACtE;AAEO,SAAS,SAAS,KAAkB;AACzC,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,GAAG;AAChC,QAAM,QAAQ,IAAI;AAClB,QAAM,QAAQ,IAAI;AAClB,QAAM,QAAQ,IAAI;AAElB,QAAM,MAAM,KAAK,IAAI,OAAO,OAAO,KAAK;AACxC,QAAM,MAAM,KAAK,IAAI,OAAO,OAAO,KAAK;AACxC,QAAM,QAAQ,MAAM;AAEpB,MAAI,IAAI;AACR,MAAI,UAAU,GAAG;AACf,QAAI,QAAQ,OAAO;AACjB,WAAM,QAAQ,SAAS,QAAS;AAAA,IAClC,WAAW,QAAQ,OAAO;AACxB,WAAK,QAAQ,SAAS,QAAQ;AAAA,IAChC,OAAO;AACL,WAAK,QAAQ,SAAS,QAAQ;AAAA,IAChC;AACA,QAAI,KAAK,MAAM,IAAI,EAAE;AACrB,QAAI,IAAI,EAAG,MAAK;AAAA,EAClB;AAEA,QAAM,KAAK,MAAM,OAAO;AACxB,QAAM,IAAI,UAAU,IAAI,IAAI,SAAS,IAAI,KAAK,IAAI,IAAI,IAAI,CAAC;AAE3D,SAAO;AAAA,IACL;AAAA,IACA,GAAG,KAAK,MAAM,IAAI,GAAG;AAAA,IACrB,GAAG,KAAK,MAAM,IAAI,GAAG;AAAA,EACvB;AACF;AAEO,SAAS,SAAS,EAAE,GAAG,GAAG,EAAE,GAAgB;AACjD,QAAM,QAAQ,IAAI;AAClB,QAAM,QAAQ,IAAI;AAElB,QAAM,KAAK,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,KAAK;AAC1C,QAAM,IAAI,KAAK,IAAI,KAAK,IAAM,IAAI,KAAM,IAAK,CAAC;AAC9C,QAAM,IAAI,QAAQ,IAAI;AAEtB,MAAI,IAAI;AACR,MAAI,IAAI;AACR,MAAI,IAAI;AAER,MAAI,IAAI,IAAI;AACV;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB,WAAW,IAAI,KAAK;AAClB;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB,WAAW,IAAI,KAAK;AAClB;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB,WAAW,IAAI,KAAK;AAClB;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB,WAAW,IAAI,KAAK;AAClB;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB,OAAO;AACL;AAAC,KAAC,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC;AAAA,EACvB;AAEA,QAAM,QAAQ,CAAC,MACb,KAAK,OAAO,IAAI,KAAK,GAAG,EACrB,SAAS,EAAE,EACX,SAAS,GAAG,GAAG;AAEpB,SAAO,IAAI,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AAC3C;AAEA,SAAS,MAAM,OAAe,KAAa,KAAqB;AAC9D,SAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;AAGO,SAAS,aAAa,YAAmC;AAC9D,QAAM,QAAQ,aAAa,UAAU;AACrC,QAAM,MAAM,SAAS,KAAK;AAC1B,QAAM,MAAM,kBAAkB,KAAK;AAEnC,QAAM,UAAU,MAAM,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI,GAAG,IAAI,EAAE;AACvE,QAAM,SAAS,SAAS;AAAA,IACtB,GAAG,IAAI;AAAA,IACP,GAAG,MAAM,IAAI,GAAG,GAAG,GAAG;AAAA,IACtB,GAAG;AAAA,EACL,CAAC;AAED,MAAI;AACJ,MAAI,OAAO,MAAM;AACf,cAAU;AAAA,EACZ,WAAW,OAAO,QAAQ,IAAI,IAAI,IAAI;AACpC,cAAU;AAAA,EACZ,OAAO;AACL,cAAU;AAAA,EACZ;AAEA,QAAM,YAAY,OAAO,OAAO,uBAAuB;AAEvD,SAAO,EAAE,SAAS,WAAW,OAAO;AACtC;AAEO,SAAS,YAAY,cAAqC;AAC/D,SAAO,aAAa,YAAY;AAClC;;;AR5CW;AAxFX,SAAS,aAAa,OAA8B;AAClD,MAAI;AACF,UAAM,cAAc,MAAM,MAAM,GAAG,EAAE,CAAC;AACtC,QAAI,CAAC,YAAa,QAAO;AACzB,UAAM,SAAS,cAAc,KAAK,OAAO,YAAY,SAAS,KAAK,KAAK,CAAC;AACzE,UAAM,SAAS,OAAO,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC1D,UAAM,OAAO,KAAK,MAAM,KAAK,MAAM,CAAC;AACpC,WAAO,OAAO,KAAK,QAAQ,WAAW,KAAK,MAAM;AAAA,EACnD,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,0BAAoD,oBAAK,SAASC,qBAAoB;AAAA,EACjG;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAG;AA5CH;AA6CE,QAAM,gBAAe,WAAM,aAAN,YAAkB;AACvC,QAAM,EAAE,QAAQ,WAAW,MAAM,IAAI,mBAAmB,WAAW,MAAM,YAAY,cAAc,6BAAM,QAAQ;AAEjH,QAAM,aAAS,uBAAQ,MAAG;AAhD5B,QAAAC;AAgD+B,wBAAYA,MAAA,+BAAO,iBAAP,OAAAA,MAAuB,SAAS;AAAA,KAAG,CAAC,+BAAO,YAAY,CAAC;AACjG,QAAM,EAAE,eAAe,mBAAmB,IAAI,UAAU;AACxD,QAAM,gBAAY,sBAA2B,IAAI;AACjD,QAAM,kBAAc,sBAAO,KAAK;AAEhC,QAAM,SAAS;AAAA,IACb,EAAE,cAAc,YAAY,SAAS,QAAQ,UAAU,YAAY,SAAS,SAAS,SAAS;AAAA,IAC9F,MAAM;AAAA,EACR;AAEA,QAAM,EAAE,gBAAgB,aAAa,IAAI;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,IACN,6BAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AAEA,+BAAU,MAAM;AACd,QAAI,OAAO;AACT,yCAAU,EAAE,MAAM,iBAAiB,SAAS,MAAM,QAAQ;AAAA,IAC5D;AAAA,EACF,GAAG,CAAC,OAAO,OAAO,CAAC;AAEnB,+BAAU,MAAM;AACd,gBAAY,UAAU;AAAA,EACxB,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,+BAAU,MAAM;AACd,QAAI,YAAY,QAAS;AACzB,QAAI,mBAAmB,QAAQ,CAAC,UAAU,QAAS;AACnD,UAAM,KAAK,UAAU;AACrB,QAAI,OAAO,GAAG,gBAAgB,UAAU;AACtC,SAAG,cAAc;AACjB,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,gBAAgB,MAAM,CAAC;AAE3B,QAAM,sBAAkB;AAAA,IACtB,CAAC,OAA2B;AAC1B,gBAAU,UAAU;AACpB,UAAI,MAAM,kBAAkB,MAAM;AAChC;AAAC,QAAC,GAAmC,SAAS;AAAA,MAChD;AAAA,IACF;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AAEA,QAAM,gBACJ,sDAAU,iBAAV,aAA2B,6BAAM,YAAW,aAAa,KAAK,QAAQ,IAAI,SAA1E,YAAmF,aAAa,MAAhG,YAAqG;AAEvG,MAAI,aAAa,CAAC,QAAQ;AACxB,WAAO,4CAAC,SAAI,WAAsB,OAAO,EAAE,OAAO,QAAQ,aAAa,UAAU,YAAY,OAAO,GAAG;AAAA,EACzG;AAEA,SACE;AAAA,IAAC,wBAAAC;AAAA,IAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,OAAO;AAAA,MACP,YAAY,MAAM;AAAA,MAClB;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB,QAAQ;AAAA,QACN,UAAU,OAAO;AAAA,QACjB,WAAW,OAAO;AAAA,QAClB,YAAY,OAAO;AAAA,MACrB;AAAA,MACA,cAAc,OAAO;AAAA,MACrB,gBAAgB,OAAO;AAAA,MACvB,aAAa,OAAO;AAAA,MACpB,UAAU;AAAA,QACR,UAAU,MAAM;AAAA,QAChB,aAAa,MAAM;AAAA,QACnB,mBAAmB;AAAA,QACnB,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,cAAa,0CAAU,eAAV,YAAwB;AAAA,QACrC,WAAU,0CAAU,YAAV,YAAqB;AAAA,QAC/B,WAAU,0CAAU,YAAV,YAAqB;AAAA,MACjC;AAAA,MACA,aAAW;AAAA,MACX,OAAO,EAAE,OAAO,QAAQ,aAAa,SAAS;AAAA,MAC9C,cAAc,MAAM,OAAO,YAAY;AAAA,MACvC,cAAc,CAAC,MAAM;AACnB,cAAM,KAAK,EAAE;AACb,YAAI,CAAC,GAAI;AACT,eAAO,iBAAiB,GAAG,aAAa,GAAG,QAAQ;AACnD,YAAI,6BAAM,UAAU;AAClB,uBAAa,GAAG,aAAa,GAAG,QAAQ;AAAA,QAC1C;AAAA,MACF;AAAA,MACA,SAAS,CAAC,MAAM;AA5ItB,YAAAD;AA6IQ,cAAM,KAAK,EAAE;AACb,eAAO,aAAYA,MAAA,yBAAI,gBAAJ,OAAAA,MAAmB,CAAC;AAAA,MACzC;AAAA,MACA,QAAQ,CAAC,MAAM;AAhJrB,YAAAA;AAiJQ,cAAM,KAAK,EAAE;AACb,eAAO,YAAWA,MAAA,yBAAI,gBAAJ,OAAAA,MAAmB,CAAC;AAAA,MACxC;AAAA,MACA,SAAS,MAAM,OAAO,eAAe;AAAA,MACrC,gBAAgB,CAAC,MAAM;AACrB,cAAM,KAAK,EAAE;AACb,YAAI,MAAM,OAAO,GAAG,WAAW,UAAU;AACvC,6BAAmB,GAAG,MAAM;AAAA,QAC9B;AAAA,MACF;AAAA,MACA,SAAS,MAAM,OAAO,YAAY,gBAAgB;AAAA;AAAA,EACpD;AAEJ,CAAC;","names":["import_react","import_react","import_react","import_react","MediaCreatorsPlayer","_a","MuxPlayer"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
interface PlayerAsset {
|
|
4
|
+
assetId: string;
|
|
5
|
+
playbackId: string;
|
|
6
|
+
title: string;
|
|
7
|
+
/** Optional duration hint (seconds) for token TTL; defaults to 3600 */
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
interface PlayerAuth {
|
|
11
|
+
fanToken: string;
|
|
12
|
+
}
|
|
13
|
+
interface PlayerTheme {
|
|
14
|
+
primaryColor: string;
|
|
15
|
+
}
|
|
16
|
+
interface PlayerMetadata {
|
|
17
|
+
playerName?: string;
|
|
18
|
+
custom1?: string;
|
|
19
|
+
custom2?: string;
|
|
20
|
+
viewerUserId?: string;
|
|
21
|
+
}
|
|
22
|
+
interface PlayerError {
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
interface PlayerEvents {
|
|
27
|
+
onTimeUpdate?: (currentTime: number, duration: number, pct: number) => void;
|
|
28
|
+
onQuartile?: (quartile: 25 | 50 | 75 | 100) => void;
|
|
29
|
+
onPause?: (currentTime: number) => void;
|
|
30
|
+
onPlay?: (currentTime: number) => void;
|
|
31
|
+
onSeeked?: (from: number, to: number) => void;
|
|
32
|
+
onComplete?: () => void;
|
|
33
|
+
onError?: (error: PlayerError) => void;
|
|
34
|
+
onReady?: () => void;
|
|
35
|
+
onResume?: (position: number) => void;
|
|
36
|
+
}
|
|
37
|
+
interface MediaCreatorsPlayerProps extends PlayerEvents {
|
|
38
|
+
creatorId: string;
|
|
39
|
+
asset: PlayerAsset;
|
|
40
|
+
auth?: PlayerAuth;
|
|
41
|
+
theme?: PlayerTheme;
|
|
42
|
+
/** Player chrome theme name. Default: Sutro. */
|
|
43
|
+
playerTheme?: string;
|
|
44
|
+
/** Poster / thumbnail shown before playback. */
|
|
45
|
+
poster?: string;
|
|
46
|
+
metadata?: PlayerMetadata;
|
|
47
|
+
autoResume?: boolean;
|
|
48
|
+
className?: string;
|
|
49
|
+
}
|
|
50
|
+
interface PlaybackTokens {
|
|
51
|
+
video: string;
|
|
52
|
+
thumbnail: string;
|
|
53
|
+
storyboard: string;
|
|
54
|
+
expiresIn: number;
|
|
55
|
+
}
|
|
56
|
+
interface SessionToken {
|
|
57
|
+
token: string;
|
|
58
|
+
expiresAt: string;
|
|
59
|
+
}
|
|
60
|
+
interface WatchProgressRecord {
|
|
61
|
+
asset_id: string;
|
|
62
|
+
position: number;
|
|
63
|
+
duration: number;
|
|
64
|
+
completed: boolean;
|
|
65
|
+
progress_pct: number;
|
|
66
|
+
updated_at: string;
|
|
67
|
+
title?: string;
|
|
68
|
+
thumbnail_url?: string | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare const MediaCreatorsPlayer: FC<MediaCreatorsPlayerProps>;
|
|
72
|
+
|
|
73
|
+
interface UsePlaybackSessionResult {
|
|
74
|
+
tokens: PlaybackTokens | null;
|
|
75
|
+
isLoading: boolean;
|
|
76
|
+
error: Error | null;
|
|
77
|
+
}
|
|
78
|
+
declare function usePlaybackSession(creatorId: string, playbackId: string, duration: number, fanToken?: string): UsePlaybackSessionResult;
|
|
79
|
+
|
|
80
|
+
interface UseWatchProgressResult {
|
|
81
|
+
resumePosition: number | null;
|
|
82
|
+
saveProgress: (position: number, duration: number) => void;
|
|
83
|
+
authExpired: boolean;
|
|
84
|
+
}
|
|
85
|
+
declare function useWatchProgress(creatorId: string, assetId: string, fanToken: string | undefined, autoResume: boolean | undefined, onResume?: (position: number) => void): UseWatchProgressResult;
|
|
86
|
+
|
|
87
|
+
export { MediaCreatorsPlayer, type MediaCreatorsPlayerProps, type PlaybackTokens, type PlayerAsset, type PlayerAuth, type PlayerError, type PlayerEvents, type PlayerMetadata, type PlayerTheme, type SessionToken, type WatchProgressRecord, usePlaybackSession, useWatchProgress };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
interface PlayerAsset {
|
|
4
|
+
assetId: string;
|
|
5
|
+
playbackId: string;
|
|
6
|
+
title: string;
|
|
7
|
+
/** Optional duration hint (seconds) for token TTL; defaults to 3600 */
|
|
8
|
+
duration?: number;
|
|
9
|
+
}
|
|
10
|
+
interface PlayerAuth {
|
|
11
|
+
fanToken: string;
|
|
12
|
+
}
|
|
13
|
+
interface PlayerTheme {
|
|
14
|
+
primaryColor: string;
|
|
15
|
+
}
|
|
16
|
+
interface PlayerMetadata {
|
|
17
|
+
playerName?: string;
|
|
18
|
+
custom1?: string;
|
|
19
|
+
custom2?: string;
|
|
20
|
+
viewerUserId?: string;
|
|
21
|
+
}
|
|
22
|
+
interface PlayerError {
|
|
23
|
+
code: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
interface PlayerEvents {
|
|
27
|
+
onTimeUpdate?: (currentTime: number, duration: number, pct: number) => void;
|
|
28
|
+
onQuartile?: (quartile: 25 | 50 | 75 | 100) => void;
|
|
29
|
+
onPause?: (currentTime: number) => void;
|
|
30
|
+
onPlay?: (currentTime: number) => void;
|
|
31
|
+
onSeeked?: (from: number, to: number) => void;
|
|
32
|
+
onComplete?: () => void;
|
|
33
|
+
onError?: (error: PlayerError) => void;
|
|
34
|
+
onReady?: () => void;
|
|
35
|
+
onResume?: (position: number) => void;
|
|
36
|
+
}
|
|
37
|
+
interface MediaCreatorsPlayerProps extends PlayerEvents {
|
|
38
|
+
creatorId: string;
|
|
39
|
+
asset: PlayerAsset;
|
|
40
|
+
auth?: PlayerAuth;
|
|
41
|
+
theme?: PlayerTheme;
|
|
42
|
+
/** Player chrome theme name. Default: Sutro. */
|
|
43
|
+
playerTheme?: string;
|
|
44
|
+
/** Poster / thumbnail shown before playback. */
|
|
45
|
+
poster?: string;
|
|
46
|
+
metadata?: PlayerMetadata;
|
|
47
|
+
autoResume?: boolean;
|
|
48
|
+
className?: string;
|
|
49
|
+
}
|
|
50
|
+
interface PlaybackTokens {
|
|
51
|
+
video: string;
|
|
52
|
+
thumbnail: string;
|
|
53
|
+
storyboard: string;
|
|
54
|
+
expiresIn: number;
|
|
55
|
+
}
|
|
56
|
+
interface SessionToken {
|
|
57
|
+
token: string;
|
|
58
|
+
expiresAt: string;
|
|
59
|
+
}
|
|
60
|
+
interface WatchProgressRecord {
|
|
61
|
+
asset_id: string;
|
|
62
|
+
position: number;
|
|
63
|
+
duration: number;
|
|
64
|
+
completed: boolean;
|
|
65
|
+
progress_pct: number;
|
|
66
|
+
updated_at: string;
|
|
67
|
+
title?: string;
|
|
68
|
+
thumbnail_url?: string | null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare const MediaCreatorsPlayer: FC<MediaCreatorsPlayerProps>;
|
|
72
|
+
|
|
73
|
+
interface UsePlaybackSessionResult {
|
|
74
|
+
tokens: PlaybackTokens | null;
|
|
75
|
+
isLoading: boolean;
|
|
76
|
+
error: Error | null;
|
|
77
|
+
}
|
|
78
|
+
declare function usePlaybackSession(creatorId: string, playbackId: string, duration: number, fanToken?: string): UsePlaybackSessionResult;
|
|
79
|
+
|
|
80
|
+
interface UseWatchProgressResult {
|
|
81
|
+
resumePosition: number | null;
|
|
82
|
+
saveProgress: (position: number, duration: number) => void;
|
|
83
|
+
authExpired: boolean;
|
|
84
|
+
}
|
|
85
|
+
declare function useWatchProgress(creatorId: string, assetId: string, fanToken: string | undefined, autoResume: boolean | undefined, onResume?: (position: number) => void): UseWatchProgressResult;
|
|
86
|
+
|
|
87
|
+
export { MediaCreatorsPlayer, type MediaCreatorsPlayerProps, type PlaybackTokens, type PlayerAsset, type PlayerAuth, type PlayerError, type PlayerEvents, type PlayerMetadata, type PlayerTheme, type SessionToken, type WatchProgressRecord, usePlaybackSession, useWatchProgress };
|