@vexor-video/player 0.0.0-stage → 0.1.0
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 +21 -0
- package/README.md +221 -2
- package/dist/chunk-NBWDQALI.js +290 -0
- package/dist/chunk-NBWDQALI.js.map +1 -0
- package/dist/index.cjs +1312 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +306 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +120 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +991 -0
- package/dist/index.js.map +1 -0
- package/dist/vanilla-BZJjSEad.d.cts +66 -0
- package/dist/vanilla-BZJjSEad.d.ts +66 -0
- package/dist/vanilla.cjs +325 -0
- package/dist/vanilla.cjs.map +1 -0
- package/dist/vanilla.d.cts +1 -0
- package/dist/vanilla.d.ts +1 -0
- package/dist/vanilla.js +7 -0
- package/dist/vanilla.js.map +1 -0
- package/dist/vexor-player.global.js +45 -0
- package/dist/vexor-player.global.js.map +1 -0
- package/package.json +57 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/VexorPlayer.tsx","../src/hooks/useVexorPlayer.ts","../src/components/Controls.tsx","../src/components/Watermark.tsx"],"sourcesContent":["import React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport { useVexorPlayer } from \"../hooks/useVexorPlayer\";\nimport { Controls } from \"./Controls\";\nimport { Watermark } from \"./Watermark\";\nimport type { VexorPlayerProps } from \"../types\";\n\nexport const VexorPlayer: React.FC<VexorPlayerProps> = (props) => {\n const {\n poster,\n preload = \"metadata\",\n muted = false,\n loop = false,\n controls = true,\n nativeControls = false,\n watermark,\n protect = false,\n className = \"\",\n style,\n children,\n } = props;\n\n const player = useVexorPlayer(props);\n const [controlsVisible, setControlsVisible] = useState(true);\n const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const resetHideTimer = useCallback(() => {\n setControlsVisible(true);\n if (hideTimerRef.current) {\n clearTimeout(hideTimerRef.current);\n }\n if (player.isPlaying && !player.containerRef.current?.contains(document.activeElement)) {\n hideTimerRef.current = setTimeout(() => {\n setControlsVisible(false);\n }, 2500);\n }\n }, [player.isPlaying]);\n\n useEffect(() => {\n resetHideTimer();\n return () => {\n if (hideTimerRef.current) clearTimeout(hideTimerRef.current);\n };\n }, [player.isPlaying, resetHideTimer]);\n\n // Anti-tamper protection handlers\n const handleContextMenu = (e: React.MouseEvent) => {\n if (protect) {\n e.preventDefault();\n }\n };\n\n // Keyboard navigation & shortcuts\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n // Leave keystrokes to focused form controls, buttons, and custom controls.\n const target = e.target as HTMLElement;\n if (target?.closest(\"input, textarea, select, button, [contenteditable='true'], [role='slider']\")) return;\n\n const container = player.containerRef.current;\n if (!container || (!document.fullscreenElement && !container.contains(document.activeElement) && !container.matches(\":hover\"))) {\n return;\n }\n\n switch (e.key.toLowerCase()) {\n case \" \":\n case \"k\":\n e.preventDefault();\n player.togglePlay();\n resetHideTimer();\n break;\n case \"arrowleft\":\n case \"j\":\n e.preventDefault();\n player.seek(player.currentTime - 5);\n resetHideTimer();\n break;\n case \"arrowright\":\n case \"l\":\n e.preventDefault();\n player.seek(player.currentTime + 5);\n resetHideTimer();\n break;\n case \"arrowup\":\n e.preventDefault();\n player.setVolume(player.volume + 0.1);\n resetHideTimer();\n break;\n case \"arrowdown\":\n e.preventDefault();\n player.setVolume(player.volume - 0.1);\n resetHideTimer();\n break;\n case \"m\":\n e.preventDefault();\n player.toggleMute();\n resetHideTimer();\n break;\n case \"f\":\n e.preventDefault();\n player.toggleFullscreen();\n resetHideTimer();\n break;\n }\n };\n\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => window.removeEventListener(\"keydown\", handleKeyDown);\n }, [player, resetHideTimer]);\n\n return (\n <div\n ref={player.containerRef}\n className={`vexor-player ${player.isFullscreen ? \"is-fullscreen\" : \"\"} ${protect ? \"is-protected\" : \"\"} ${className}`}\n style={style}\n onMouseMove={resetHideTimer}\n onMouseLeave={(event) => {\n if (player.isPlaying && !event.currentTarget.contains(document.activeElement)) {\n setControlsVisible(false);\n }\n }}\n onFocusCapture={() => {\n if (hideTimerRef.current) clearTimeout(hideTimerRef.current);\n setControlsVisible(true);\n }}\n onBlurCapture={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) resetHideTimer();\n }}\n onContextMenu={handleContextMenu}\n tabIndex={0}\n role=\"region\"\n aria-label=\"Video Player\"\n >\n <video\n ref={player.videoRef}\n className=\"vexor-video\"\n poster={poster}\n preload={preload}\n muted={muted}\n loop={loop}\n playsInline\n controls={nativeControls}\n onClick={nativeControls ? undefined : player.togglePlay}\n />\n\n {/* Watermark Overlay */}\n {watermark && <Watermark config={watermark} />}\n\n {/* Buffering Spinner */}\n {player.isBuffering && !player.error && (\n <div className=\"vexor-spinner-overlay\" aria-label=\"Buffering video\">\n <div className=\"vexor-spinner\" />\n </div>\n )}\n\n {/* Center Big Play Button (when paused, not buffering, and custom controls active) */}\n {!nativeControls && controls && !player.isPlaying && !player.isBuffering && !player.error && (\n <button type=\"button\" className=\"vexor-center-play-overlay\" onClick={player.togglePlay} aria-label=\"Play video\">\n <span className=\"vexor-center-play-button\" aria-hidden=\"true\">\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <polygon points=\"6,4 20,12 6,20\" />\n </svg>\n </span>\n </button>\n )}\n\n {/* Custom Controls Bar */}\n {!nativeControls && controls && !player.error && (\n <Controls\n isPlaying={player.isPlaying}\n isBuffering={player.isBuffering}\n currentTime={player.currentTime}\n duration={player.duration}\n buffered={player.buffered}\n volume={player.volume}\n isMuted={player.isMuted}\n isFullscreen={player.isFullscreen}\n qualityLevels={player.qualityLevels}\n currentQuality={player.currentQuality}\n playbackRate={player.playbackRate}\n visible={controlsVisible}\n onPlayPause={player.togglePlay}\n onSeek={player.seek}\n onVolumeChange={player.setVolume}\n onToggleMute={player.toggleMute}\n onQualityChange={player.setQuality}\n onPlaybackRateChange={player.setPlaybackRate}\n onToggleFullscreen={player.toggleFullscreen}\n onTogglePiP={player.togglePiP}\n />\n )}\n\n {/* Error Overlay */}\n {player.error && (\n <div className=\"vexor-error-overlay\">\n <div className=\"vexor-error-title\">Playback Error</div>\n <div className=\"vexor-error-message\">{player.error.message}</div>\n <button type=\"button\" className=\"vexor-retry-button\" onClick={player.retry}>\n Retry Playback\n </button>\n </div>\n )}\n\n {/* Custom Child Overlays */}\n {children}\n </div>\n );\n};\n","import { useEffect, useRef, useState, useCallback } from \"react\";\nimport Hls from \"hls.js\";\nimport type { QualityLevel, VexorPlayerProps } from \"../types\";\nimport { startNativeTokenRefresh } from \"../native-token-refresh\";\n\nexport interface UseVexorPlayerReturn {\n videoRef: React.RefObject<HTMLVideoElement | null>;\n containerRef: React.RefObject<HTMLDivElement | null>;\n isPlaying: boolean;\n isBuffering: boolean;\n currentTime: number;\n duration: number;\n buffered: number;\n volume: number;\n isMuted: boolean;\n isFullscreen: boolean;\n qualityLevels: QualityLevel[];\n currentQuality: number;\n playbackRate: number;\n error: { code: string; message: string; fatal: boolean } | null;\n play: () => Promise<void>;\n pause: () => void;\n togglePlay: () => void;\n seek: (time: number) => void;\n setVolume: (vol: number) => void;\n toggleMute: () => void;\n setQuality: (levelIndex: number) => void;\n setPlaybackRate: (rate: number) => void;\n toggleFullscreen: () => void;\n togglePiP: () => void;\n retry: () => void;\n}\n\nexport function useVexorPlayer({\n src,\n assetId,\n token,\n baseUrl = \"https://api.vexor.et\",\n fallbackSrc,\n autoPlay = false,\n muted = false,\n refreshToken,\n onReady,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n onQualityChange,\n}: VexorPlayerProps): UseVexorPlayerReturn {\n const videoRef = useRef<HTMLVideoElement | null>(null);\n const containerRef = useRef<HTMLDivElement | null>(null);\n const hlsRef = useRef<Hls | null>(null);\n const callbacksRef = useRef({ onReady, onPlay, onPause, onEnded, onTimeUpdate, onError, onQualityChange, refreshToken });\n callbacksRef.current = { onReady, onPlay, onPause, onEnded, onTimeUpdate, onError, onQualityChange, refreshToken };\n const activeParamsRef = useRef(\"\");\n const refreshPromiseRef = useRef<Promise<string> | null>(null);\n const resumeAfterRefreshRef = useRef<{ time: number; shouldPlay: boolean } | null>(null);\n\n const [isPlaying, setIsPlaying] = useState(false);\n const [isBuffering, setIsBuffering] = useState(true);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [buffered, setBuffered] = useState(0);\n const [volume, setVolumeState] = useState(1);\n const [isMuted, setIsMuted] = useState(muted);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [qualityLevels, setQualityLevels] = useState<QualityLevel[]>([]);\n const [currentQuality, setCurrentQuality] = useState(-1);\n const [playbackRate, setPlaybackRateState] = useState(1);\n const [error, setError] = useState<{ code: string; message: string; fatal: boolean } | null>(null);\n const [reloadTrigger, setReloadTrigger] = useState(0);\n\n // Determine effective playback manifest URL\n const activeUrl = src\n ? src\n : assetId\n ? `${baseUrl.replace(/\\/+$/, \"\")}/v1/playback/${encodeURIComponent(assetId)}/master.m3u8${token ? `?token=${encodeURIComponent(token)}` : \"\"}`\n : \"\";\n\n const destroyHls = useCallback(() => {\n if (hlsRef.current) {\n hlsRef.current.destroy();\n hlsRef.current = null;\n }\n }, []);\n\n const retry = useCallback(() => {\n setError(null);\n setIsBuffering(true);\n setReloadTrigger((v) => v + 1);\n }, []);\n\n // Initialize HLS & Media Source\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n let cleanupFallback = () => {};\n const loadFallback = () => {\n if (!fallbackSrc) return false;\n destroyHls();\n setIsBuffering(true);\n video.src = fallbackSrc;\n const onLoaded = () => {\n setIsBuffering(false);\n callbacksRef.current.onReady?.();\n };\n const onError = () => {\n const errObj = {\n code: \"FALLBACK_PLAYBACK_ERROR\",\n message: \"Unable to load fallback media source\",\n fatal: true,\n };\n setError(errObj);\n callbacksRef.current.onError?.(errObj);\n setIsBuffering(false);\n };\n video.addEventListener(\"loadeddata\", onLoaded);\n video.addEventListener(\"error\", onError);\n cleanupFallback = () => {\n video.removeEventListener(\"loadeddata\", onLoaded);\n video.removeEventListener(\"error\", onError);\n };\n video.load();\n return true;\n };\n const setFatalError = (code: string, message: string) => {\n destroyHls();\n const errObj = { code, message, fatal: true };\n setError(errObj);\n callbacksRef.current.onError?.(errObj);\n setIsBuffering(false);\n };\n\n destroyHls();\n setError(null);\n setCurrentTime(0);\n setDuration(0);\n setBuffered(0);\n setQualityLevels([]);\n setCurrentQuality(-1);\n setIsBuffering(Boolean(activeUrl));\n if (!activeUrl) {\n video.pause();\n video.removeAttribute(\"src\");\n video.load();\n return;\n }\n\n const isHlsUrl = activeUrl.includes(\".m3u8\") || activeUrl.includes(\"/playback/\");\n let fatalRecoveryAttempts = 0;\n\n if (isHlsUrl && Hls.isSupported()) {\n activeParamsRef.current = activeUrl.includes(\"?\") ? activeUrl.substring(activeUrl.indexOf(\"?\")) : \"\";\n\n class VexorLoader extends Hls.DefaultConfig.loader {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n load(context: any, config: any, callbacks: any) {\n // Propagate token query params to child playlists and segments if not present\n const activeParams = activeParamsRef.current;\n if (activeParams && context?.url) {\n try {\n const sourceUrl = new URL(activeUrl, window.location.href);\n const childUrl = new URL(context.url, sourceUrl);\n if (childUrl.origin === sourceUrl.origin && !childUrl.searchParams.has(\"token\")) {\n new URLSearchParams(activeParams).forEach((value, key) => {\n if (!childUrl.searchParams.has(key)) childUrl.searchParams.set(key, value);\n });\n context.url = childUrl.toString();\n }\n } catch {\n // Leave an unparseable child URL untouched; the loader will report failures normally.\n }\n }\n super.load(context, config, callbacks);\n }\n }\n\n const hls = new Hls({\n startFragPrefetch: true,\n maxBufferLength: 30,\n maxMaxBufferLength: 60,\n enableWorker: true,\n lowLatencyMode: true,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n loader: VexorLoader as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n fLoader: VexorLoader as any,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n pLoader: VexorLoader as any,\n });\n hlsRef.current = hls;\n\n hls.loadSource(activeUrl);\n hls.attachMedia(video);\n\n hls.on(Hls.Events.MANIFEST_PARSED, (_event, data) => {\n if (hlsRef.current !== hls) return;\n setIsBuffering(false);\n const levels: QualityLevel[] = data.levels.map((lvl, index) => {\n let label = `${lvl.height}p`;\n if (lvl.height === 0 || lvl.videoCodec === undefined) {\n label = \"Audio only\";\n }\n return {\n id: index,\n height: lvl.height,\n width: lvl.width,\n bitrate: lvl.bitrate,\n label,\n };\n });\n setQualityLevels(levels);\n callbacksRef.current.onReady?.();\n const resume = resumeAfterRefreshRef.current;\n if (resume) {\n resumeAfterRefreshRef.current = null;\n if (Number.isFinite(resume.time) && resume.time > 0) video.currentTime = resume.time;\n if (resume.shouldPlay) video.play().catch(() => {});\n }\n if (autoPlay) {\n video.play().catch(() => {\n // Autoplay might be blocked by browser policy; user can interact to start\n });\n }\n });\n\n hls.on(Hls.Events.LEVEL_SWITCHED, (_event, data) => {\n if (hlsRef.current !== hls) return;\n setCurrentQuality(hls.autoLevelEnabled ? -1 : data.level);\n callbacksRef.current.onQualityChange?.(hls.autoLevelEnabled ? -1 : data.level);\n });\n\n hls.on(Hls.Events.ERROR, async (_event, data) => {\n if (hlsRef.current !== hls) return;\n // Handle token expiry (HTTP 401 or 403 on key/segment)\n if (\n (data.response?.code === 401 || data.response?.code === 403) &&\n callbacksRef.current.refreshToken\n ) {\n try {\n if (!refreshPromiseRef.current) {\n refreshPromiseRef.current = callbacksRef.current.refreshToken();\n }\n const newToken = await refreshPromiseRef.current;\n refreshPromiseRef.current = null;\n if (newToken && hlsRef.current === hls) {\n resumeAfterRefreshRef.current = {\n time: video.currentTime,\n shouldPlay: !video.paused,\n };\n const url = new URL(activeUrl, window.location.href);\n url.searchParams.set(\"token\", newToken);\n activeParamsRef.current = url.search;\n hls.loadSource(url.toString());\n hls.startLoad();\n return;\n }\n } catch {\n refreshPromiseRef.current = null;\n // Fall through to standard error handling if token refresh fails\n }\n }\n\n if (hlsRef.current !== hls) return;\n\n if (data.fatal) {\n switch (data.type) {\n case Hls.ErrorTypes.NETWORK_ERROR:\n if (fatalRecoveryAttempts++ < 1) {\n hls.startLoad();\n } else if (!loadFallback()) {\n setFatalError(data.details || \"NETWORK_ERROR\", `Failed to stream video: ${data.details || \"network error\"}`);\n }\n break;\n case Hls.ErrorTypes.MEDIA_ERROR:\n if (fatalRecoveryAttempts++ < 1) {\n hls.recoverMediaError();\n } else if (!loadFallback()) {\n setFatalError(data.details || \"MEDIA_ERROR\", `Failed to stream video: ${data.details || \"media error\"}`);\n }\n break;\n default:\n if (!loadFallback()) {\n setFatalError(data.details || \"MEDIA_PLAYBACK_ERROR\", `Failed to stream video: ${data.details || \"network error\"}`);\n }\n break;\n }\n }\n });\n } else if (isHlsUrl && video.canPlayType(\"application/vnd.apple.mpegurl\")) {\n // Safari / iOS Native HLS\n video.src = activeUrl;\n const nativeTokenRefresh = startNativeTokenRefresh(\n video,\n activeUrl,\n callbacksRef.current.refreshToken,\n );\n const onLoaded = () => {\n setIsBuffering(false);\n callbacksRef.current.onReady?.();\n if (autoPlay) {\n video.play().catch(() => {});\n }\n };\n const onNativeError = () => {\n void nativeTokenRefresh.refreshNow().then((refreshed) => {\n if (refreshed || hlsRef.current) return;\n if (loadFallback()) {\n video.removeEventListener(\"error\", onNativeError);\n return;\n }\n setFatalError(\"NATIVE_HLS_ERROR\", \"Failed to play native HLS stream\");\n });\n };\n video.addEventListener(\"loadedmetadata\", onLoaded);\n video.addEventListener(\"error\", onNativeError);\n return () => {\n nativeTokenRefresh.stop();\n video.removeEventListener(\"loadedmetadata\", onLoaded);\n video.removeEventListener(\"error\", onNativeError);\n };\n } else if (!isHlsUrl) {\n // Standard MP4 progressive playback\n video.src = activeUrl;\n const onLoaded = () => {\n setIsBuffering(false);\n onReady?.();\n };\n const onErr = () => {\n const errObj = {\n code: \"DIRECT_PLAYBACK_ERROR\",\n message: \"Unable to load media source\",\n fatal: true,\n };\n setError(errObj);\n callbacksRef.current.onError?.(errObj);\n setIsBuffering(false);\n };\n video.addEventListener(\"loadeddata\", onLoaded);\n video.addEventListener(\"error\", onErr);\n return () => {\n video.removeEventListener(\"loadeddata\", onLoaded);\n video.removeEventListener(\"error\", onErr);\n };\n } else if (fallbackSrc) {\n // HLS unsupported: use the configured progressive fallback.\n loadFallback();\n } else {\n const errObj = {\n code: \"HLS_UNSUPPORTED\",\n message: \"This browser does not support HLS playback and no fallback source was provided\",\n fatal: true,\n };\n setError(errObj);\n callbacksRef.current.onError?.(errObj);\n setIsBuffering(false);\n }\n\n return () => {\n cleanupFallback();\n destroyHls();\n };\n }, [activeUrl, fallbackSrc, autoPlay, reloadTrigger, destroyHls]);\n\n // Video Element Event Listeners\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const onPlayEvent = () => {\n setIsPlaying(true);\n callbacksRef.current.onPlay?.();\n };\n\n const onPauseEvent = () => {\n setIsPlaying(false);\n callbacksRef.current.onPause?.();\n };\n\n const onEndedEvent = () => {\n setIsPlaying(false);\n callbacksRef.current.onEnded?.();\n };\n\n const onWaiting = () => setIsBuffering(true);\n const onPlaying = () => setIsBuffering(false);\n\n const onTimeUpdateEvent = () => {\n setCurrentTime(video.currentTime);\n callbacksRef.current.onTimeUpdate?.(video.currentTime, video.duration);\n\n // Calculate buffer progress\n if (video.buffered.length > 0 && video.duration > 0) {\n for (let i = video.buffered.length - 1; i >= 0; i--) {\n if (video.buffered.start(i) <= video.currentTime) {\n setBuffered((video.buffered.end(i) / video.duration) * 100);\n break;\n }\n }\n }\n };\n\n const onDurationChange = () => setDuration(video.duration || 0);\n const onVolumeChangeEvent = () => {\n setVolumeState(video.volume);\n setIsMuted(video.muted);\n };\n\n video.addEventListener(\"play\", onPlayEvent);\n video.addEventListener(\"pause\", onPauseEvent);\n video.addEventListener(\"ended\", onEndedEvent);\n video.addEventListener(\"waiting\", onWaiting);\n video.addEventListener(\"playing\", onPlaying);\n video.addEventListener(\"timeupdate\", onTimeUpdateEvent);\n video.addEventListener(\"durationchange\", onDurationChange);\n video.addEventListener(\"volumechange\", onVolumeChangeEvent);\n\n return () => {\n video.removeEventListener(\"play\", onPlayEvent);\n video.removeEventListener(\"pause\", onPauseEvent);\n video.removeEventListener(\"ended\", onEndedEvent);\n video.removeEventListener(\"waiting\", onWaiting);\n video.removeEventListener(\"playing\", onPlaying);\n video.removeEventListener(\"timeupdate\", onTimeUpdateEvent);\n video.removeEventListener(\"durationchange\", onDurationChange);\n video.removeEventListener(\"volumechange\", onVolumeChangeEvent);\n };\n }, []);\n\n // Fullscreen change listener\n useEffect(() => {\n const handleFullscreenChange = () => {\n setIsFullscreen(Boolean(document.fullscreenElement));\n };\n document.addEventListener(\"fullscreenchange\", handleFullscreenChange);\n return () => document.removeEventListener(\"fullscreenchange\", handleFullscreenChange);\n }, []);\n\n // Player Actions\n const play = useCallback(async () => {\n if (videoRef.current) {\n await videoRef.current.play();\n }\n }, []);\n\n const pause = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.pause();\n }\n }, []);\n\n const togglePlay = useCallback(() => {\n if (!videoRef.current) return;\n if (videoRef.current.paused) {\n videoRef.current.play().catch(() => {});\n } else {\n videoRef.current.pause();\n }\n }, []);\n\n const seek = useCallback((time: number) => {\n if (videoRef.current) {\n videoRef.current.currentTime = Math.max(0, Math.min(time, videoRef.current.duration || time));\n }\n }, []);\n\n const setVolume = useCallback((vol: number) => {\n if (videoRef.current) {\n const clamped = Math.max(0, Math.min(1, vol));\n videoRef.current.volume = clamped;\n videoRef.current.muted = clamped === 0;\n }\n }, []);\n\n const toggleMute = useCallback(() => {\n if (videoRef.current) {\n videoRef.current.muted = !videoRef.current.muted;\n }\n }, []);\n\n const setQuality = useCallback((levelIndex: number) => {\n if (hlsRef.current) {\n if (levelIndex === -1) {\n hlsRef.current.currentLevel = -1; // Auto\n } else {\n hlsRef.current.currentLevel = levelIndex;\n }\n setCurrentQuality(levelIndex);\n }\n }, []);\n\n const setPlaybackRate = useCallback((rate: number) => {\n if (videoRef.current) {\n videoRef.current.playbackRate = rate;\n setPlaybackRateState(rate);\n }\n }, []);\n\n const toggleFullscreen = useCallback(() => {\n const container = containerRef.current || videoRef.current;\n if (!container) return;\n\n if (!document.fullscreenElement) {\n container.requestFullscreen().catch(() => {});\n } else {\n document.exitFullscreen().catch(() => {});\n }\n }, []);\n\n const togglePiP = useCallback(async () => {\n const video = videoRef.current;\n if (!video) return;\n\n try {\n if (document.pictureInPictureElement) {\n await document.exitPictureInPicture();\n } else if (document.pictureInPictureEnabled) {\n await video.requestPictureInPicture();\n }\n } catch {\n // Ignored\n }\n }, []);\n\n return {\n videoRef,\n containerRef,\n isPlaying,\n isBuffering,\n currentTime,\n duration,\n buffered,\n volume,\n isMuted,\n isFullscreen,\n qualityLevels,\n currentQuality,\n playbackRate,\n error,\n play,\n pause,\n togglePlay,\n seek,\n setVolume,\n toggleMute,\n setQuality,\n setPlaybackRate,\n toggleFullscreen,\n togglePiP,\n retry,\n };\n}\n","import React, { useState, useRef, useEffect, useCallback } from \"react\";\nimport type { QualityLevel } from \"../types\";\n\ninterface ControlsProps {\n isPlaying: boolean;\n isBuffering: boolean;\n currentTime: number;\n duration: number;\n buffered: number;\n volume: number;\n isMuted: boolean;\n isFullscreen: boolean;\n qualityLevels: QualityLevel[];\n currentQuality: number;\n playbackRate: number;\n visible: boolean;\n onPlayPause: () => void;\n onSeek: (time: number) => void;\n onVolumeChange: (volume: number) => void;\n onToggleMute: () => void;\n onQualityChange: (levelIndex: number) => void;\n onPlaybackRateChange: (rate: number) => void;\n onToggleFullscreen: () => void;\n onTogglePiP: () => void;\n}\n\nfunction formatTime(seconds: number): string {\n if (isNaN(seconds) || seconds < 0) return \"0:00\";\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = Math.floor(seconds % 60);\n if (h > 0) {\n return `${h}:${m.toString().padStart(2, \"0\")}:${s.toString().padStart(2, \"0\")}`;\n }\n return `${m}:${s.toString().padStart(2, \"0\")}`;\n}\n\nexport const Controls: React.FC<ControlsProps> = ({\n isPlaying,\n currentTime,\n duration,\n buffered,\n volume,\n isMuted,\n isFullscreen,\n qualityLevels,\n currentQuality,\n playbackRate,\n visible,\n onPlayPause,\n onSeek,\n onVolumeChange,\n onToggleMute,\n onQualityChange,\n onPlaybackRateChange,\n onToggleFullscreen,\n onTogglePiP,\n}) => {\n const [showQualityMenu, setShowQualityMenu] = useState(false);\n const [showSpeedMenu, setShowSpeedMenu] = useState(false);\n const progressRef = useRef<HTMLDivElement>(null);\n const [isScrubbing, setIsScrubbing] = useState(false);\n const [pictureInPictureSupported, setPictureInPictureSupported] = useState(false);\n\n useEffect(() => {\n setPictureInPictureSupported(\n typeof document !== \"undefined\" && document.pictureInPictureEnabled\n );\n }, []);\n\n const playedPercent = duration > 0 ? (currentTime / duration) * 100 : 0;\n const speedOptions = [0.5, 0.75, 1, 1.25, 1.5, 2];\n\n const handleScrub = useCallback(\n (e: React.PointerEvent<HTMLDivElement>) => {\n if (!progressRef.current || duration <= 0) return;\n const rect = progressRef.current.getBoundingClientRect();\n const pos = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));\n onSeek(pos * duration);\n },\n [duration, onSeek]\n );\n\n const handlePointerDown = (e: React.PointerEvent<HTMLDivElement>) => {\n if (e.button !== 0) return;\n e.currentTarget.setPointerCapture(e.pointerId);\n setIsScrubbing(true);\n handleScrub(e);\n };\n\n const handleTimelineKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (duration <= 0) return;\n const step = e.shiftKey ? 30 : 5;\n let nextTime: number | undefined;\n if (e.key === \"ArrowLeft\") nextTime = currentTime - step;\n if (e.key === \"ArrowRight\") nextTime = currentTime + step;\n if (e.key === \"Home\") nextTime = 0;\n if (e.key === \"End\") nextTime = duration;\n if (nextTime !== undefined) {\n e.preventDefault();\n onSeek(Math.max(0, Math.min(duration, nextTime)));\n }\n };\n\n return (\n <div className={`vexor-controls ${visible ? \"\" : \"is-hidden\"}`}>\n {/* Timeline Scrubber */}\n <div\n className=\"vexor-progress-container\"\n ref={progressRef}\n onPointerDown={handlePointerDown}\n onPointerMove={(event) => { if (isScrubbing) handleScrub(event); }}\n onPointerUp={(event) => {\n if (progressRef.current?.hasPointerCapture(event.pointerId)) {\n progressRef.current.releasePointerCapture(event.pointerId);\n }\n setIsScrubbing(false);\n }}\n onPointerCancel={() => setIsScrubbing(false)}\n onKeyDown={handleTimelineKeyDown}\n tabIndex={0}\n role=\"slider\"\n aria-label=\"Video timeline\"\n aria-valuemin={0}\n aria-valuemax={duration}\n aria-valuenow={Math.min(currentTime, duration || 0)}\n aria-valuetext={`${formatTime(currentTime)} of ${formatTime(duration)}`}\n >\n <div className=\"vexor-progress-track\">\n <div className=\"vexor-progress-buffer\" style={{ width: `${buffered}%` }} />\n <div className=\"vexor-progress-played\" style={{ width: `${playedPercent}%` }} />\n <div className=\"vexor-progress-thumb\" style={{ left: `${playedPercent}%` }} />\n </div>\n </div>\n\n {/* Control Buttons Row */}\n <div className=\"vexor-controls-row\">\n {/* Left Side: Play/Pause, Volume, Time */}\n <div className=\"vexor-controls-group\">\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={onPlayPause}\n aria-label={isPlaying ? \"Pause\" : \"Play\"}\n >\n {isPlaying ? (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n <rect x=\"14\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" />\n </svg>\n ) : (\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n )}\n </button>\n\n <div className=\"vexor-volume-wrapper\">\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={onToggleMute}\n aria-label={isMuted ? \"Unmute\" : \"Mute\"}\n >\n {isMuted || volume === 0 ? (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" />\n <line x1=\"23\" y1=\"9\" x2=\"17\" y2=\"15\" />\n <line x1=\"17\" y1=\"9\" x2=\"23\" y2=\"15\" />\n </svg>\n ) : (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <polygon points=\"11 5 6 9 2 9 2 15 6 15 11 19 11 5\" />\n <path d=\"M15.54 8.46a5 5 0 0 1 0 7.07\" />\n <path d=\"M19.07 4.93a10 10 0 0 1 0 14.14\" />\n </svg>\n )}\n </button>\n <input\n type=\"range\"\n min=\"0\"\n max=\"1\"\n step=\"0.05\"\n value={isMuted ? 0 : volume}\n onChange={(e) => onVolumeChange(parseFloat(e.target.value))}\n className=\"vexor-volume-slider\"\n aria-label=\"Volume slider\"\n />\n </div>\n\n <span className=\"vexor-time-display\">\n {formatTime(currentTime)} / {formatTime(duration)}\n </span>\n </div>\n\n {/* Right Side: Speed, Quality, PiP, Fullscreen */}\n <div className=\"vexor-controls-group\">\n {/* Speed Selector */}\n <div className=\"vexor-menu-anchor\">\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={() => {\n setShowSpeedMenu(!showSpeedMenu);\n setShowQualityMenu(false);\n }}\n aria-label=\"Playback speed\"\n style={{ fontSize: \"11px\", fontWeight: 600, padding: \"4px 8px\" }}\n >\n {playbackRate === 1 ? \"1x\" : `${playbackRate}x`}\n </button>\n\n {showSpeedMenu && (\n <div className=\"vexor-menu-dropdown\">\n {speedOptions.map((rate) => (\n <button\n key={rate}\n type=\"button\"\n className={`vexor-menu-item ${playbackRate === rate ? \"is-active\" : \"\"}`}\n onClick={() => {\n onPlaybackRateChange(rate);\n setShowSpeedMenu(false);\n }}\n >\n <span>{rate}x</span>\n {playbackRate === rate && <span>✓</span>}\n </button>\n ))}\n </div>\n )}\n </div>\n\n {/* Quality Levels Selector */}\n {qualityLevels.length > 0 && (\n <div className=\"vexor-menu-anchor\">\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={() => {\n setShowQualityMenu(!showQualityMenu);\n setShowSpeedMenu(false);\n }}\n aria-label=\"Video quality\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n <path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n </svg>\n </button>\n\n {showQualityMenu && (\n <div className=\"vexor-menu-dropdown\">\n <button\n type=\"button\"\n className={`vexor-menu-item ${currentQuality === -1 ? \"is-active\" : \"\"}`}\n onClick={() => {\n onQualityChange(-1);\n setShowQualityMenu(false);\n }}\n >\n <span>Auto</span>\n {currentQuality === -1 && <span>✓</span>}\n </button>\n {qualityLevels.map((lvl) => (\n <button\n key={lvl.id}\n type=\"button\"\n className={`vexor-menu-item ${currentQuality === lvl.id ? \"is-active\" : \"\"}`}\n onClick={() => {\n onQualityChange(lvl.id);\n setShowQualityMenu(false);\n }}\n >\n <span>{lvl.label}</span>\n {currentQuality === lvl.id && <span>✓</span>}\n </button>\n ))}\n </div>\n )}\n </div>\n )}\n\n {/* Picture in Picture */}\n {pictureInPictureSupported && (\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={onTogglePiP}\n aria-label=\"Picture in picture\"\n >\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <rect x=\"2\" y=\"3\" width=\"20\" height=\"14\" rx=\"2\" />\n <rect x=\"12\" y=\"9\" width=\"8\" height=\"6\" rx=\"1\" fill=\"currentColor\" fillOpacity=\"0.4\" />\n </svg>\n </button>\n )}\n\n {/* Fullscreen Toggle */}\n <button\n type=\"button\"\n className=\"vexor-btn\"\n onClick={onToggleFullscreen}\n aria-label={isFullscreen ? \"Exit fullscreen\" : \"Enter fullscreen\"}\n >\n {isFullscreen ? (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\n </svg>\n ) : (\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7\" />\n </svg>\n )}\n </button>\n </div>\n </div>\n </div>\n );\n};\n","import React, { useEffect, useState, useMemo } from \"react\";\nimport type { WatermarkConfig } from \"../types\";\n\ninterface WatermarkProps {\n config?: string | WatermarkConfig;\n}\n\nexport const Watermark: React.FC<WatermarkProps> = ({ config }) => {\n const parsedConfig: WatermarkConfig | null = useMemo(() => {\n if (!config) return null;\n if (typeof config === \"string\") {\n return { text: config, position: \"dynamic\", intervalSeconds: 15, opacity: 0.35 };\n }\n return {\n position: \"dynamic\",\n intervalSeconds: 15,\n opacity: 0.35,\n ...config,\n };\n }, [config]);\n\n const [coords, setCoords] = useState<{ top: string; left: string }>({ top: \"15%\", left: \"15%\" });\n\n useEffect(() => {\n if (!parsedConfig || parsedConfig.position !== \"dynamic\") return;\n\n const intervalMs = Math.max(2, parsedConfig.intervalSeconds || 15) * 1000;\n const updatePosition = () => {\n // Keep watermark inside 10% - 80% range so it doesn't clip\n const top = Math.floor(10 + Math.random() * 70);\n const left = Math.floor(10 + Math.random() * 70);\n setCoords({ top: `${top}%`, left: `${left}%` });\n };\n\n updatePosition();\n const timer = setInterval(updatePosition, intervalMs);\n return () => clearInterval(timer);\n }, [parsedConfig?.position, parsedConfig?.intervalSeconds]);\n\n const getPositionStyles = (): React.CSSProperties => {\n switch (parsedConfig?.position) {\n case \"top-left\":\n return { top: \"16px\", left: \"16px\" };\n case \"top-right\":\n return { top: \"16px\", right: \"16px\" };\n case \"bottom-left\":\n return { bottom: \"48px\", left: \"16px\" };\n case \"bottom-right\":\n return { bottom: \"48px\", right: \"16px\" };\n case \"center\":\n return { top: \"50%\", left: \"50%\", transform: \"translate(-50%, -50%)\" };\n case \"dynamic\":\n default:\n return { top: coords.top, left: coords.left };\n }\n };\n\n if (!parsedConfig) return null;\n\n return (\n <div\n className=\"vexor-watermark\"\n style={{\n ...getPositionStyles(),\n opacity: Math.max(0, Math.min(1, parsedConfig.opacity ?? 0.35)),\n fontSize: parsedConfig.fontSize ?? \"12px\",\n color: parsedConfig.color ?? \"#ffffff\",\n }}\n aria-hidden=\"true\"\n >\n {parsedConfig.text}\n </div>\n );\n};\n"],"mappings":"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","names":["useState","useEffect","useRef","useCallback","onError","useState","useRef","useEffect","useCallback","useEffect","useState","jsx","jsx","jsxs","useState","useRef","useCallback","useEffect"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
interface QualityLevel {
|
|
2
|
+
/** Internal Hls.js quality level index (-1 for Auto) */
|
|
3
|
+
id: number;
|
|
4
|
+
/** Video frame height (e.g. 1080, 720, 480, 360, 240, or 0 for audio) */
|
|
5
|
+
height: number;
|
|
6
|
+
/** Video frame width */
|
|
7
|
+
width: number;
|
|
8
|
+
/** Stream bitrate in bits/sec */
|
|
9
|
+
bitrate: number;
|
|
10
|
+
/** Human-readable label (e.g. "1080p", "720p", "Audio only") */
|
|
11
|
+
label: string;
|
|
12
|
+
}
|
|
13
|
+
interface WatermarkConfig {
|
|
14
|
+
/** Watermark text, such as a viewer identifier */
|
|
15
|
+
text: string;
|
|
16
|
+
/** Position strategy. Dynamic movement can make cropping less convenient. */
|
|
17
|
+
position?: "dynamic" | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
|
18
|
+
/** Reposition interval in seconds when position is dynamic (default: 15) */
|
|
19
|
+
intervalSeconds?: number;
|
|
20
|
+
/** Text opacity from 0.0 to 1.0 (default: 0.35) */
|
|
21
|
+
opacity?: number;
|
|
22
|
+
/** Font size (e.g. "12px", 12) */
|
|
23
|
+
fontSize?: number | string;
|
|
24
|
+
/** Text color (default: "#ffffff") */
|
|
25
|
+
color?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface VexorVanillaPlayerOptions {
|
|
29
|
+
src?: string;
|
|
30
|
+
assetId?: string;
|
|
31
|
+
token?: string;
|
|
32
|
+
baseUrl?: string;
|
|
33
|
+
poster?: string;
|
|
34
|
+
autoPlay?: boolean;
|
|
35
|
+
muted?: boolean;
|
|
36
|
+
loop?: boolean;
|
|
37
|
+
controls?: boolean;
|
|
38
|
+
preload?: "auto" | "metadata" | "none";
|
|
39
|
+
fallbackSrc?: string;
|
|
40
|
+
watermark?: string | WatermarkConfig;
|
|
41
|
+
/** Casual-copy deterrent only; does not prevent capture or guarantee access control. */
|
|
42
|
+
protect?: boolean;
|
|
43
|
+
refreshToken?: () => Promise<string>;
|
|
44
|
+
onReady?: () => void;
|
|
45
|
+
onPlay?: () => void;
|
|
46
|
+
onPause?: () => void;
|
|
47
|
+
onEnded?: () => void;
|
|
48
|
+
onTimeUpdate?: (currentTime: number, duration: number) => void;
|
|
49
|
+
onError?: (error: {
|
|
50
|
+
code: string;
|
|
51
|
+
message: string;
|
|
52
|
+
fatal: boolean;
|
|
53
|
+
}) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface VanillaPlayerInstance {
|
|
57
|
+
play: () => Promise<void>;
|
|
58
|
+
pause: () => void;
|
|
59
|
+
seek: (seconds: number) => void;
|
|
60
|
+
destroy: () => void;
|
|
61
|
+
getElement: () => HTMLVideoElement;
|
|
62
|
+
}
|
|
63
|
+
/** Mount the Vexor player into an element without requiring React. */
|
|
64
|
+
declare function createVexorPlayer(target: HTMLElement | string, options: VexorVanillaPlayerOptions): VanillaPlayerInstance;
|
|
65
|
+
|
|
66
|
+
export { type QualityLevel as Q, type VanillaPlayerInstance as V, type WatermarkConfig as W, type VexorVanillaPlayerOptions as a, createVexorPlayer as c };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
interface QualityLevel {
|
|
2
|
+
/** Internal Hls.js quality level index (-1 for Auto) */
|
|
3
|
+
id: number;
|
|
4
|
+
/** Video frame height (e.g. 1080, 720, 480, 360, 240, or 0 for audio) */
|
|
5
|
+
height: number;
|
|
6
|
+
/** Video frame width */
|
|
7
|
+
width: number;
|
|
8
|
+
/** Stream bitrate in bits/sec */
|
|
9
|
+
bitrate: number;
|
|
10
|
+
/** Human-readable label (e.g. "1080p", "720p", "Audio only") */
|
|
11
|
+
label: string;
|
|
12
|
+
}
|
|
13
|
+
interface WatermarkConfig {
|
|
14
|
+
/** Watermark text, such as a viewer identifier */
|
|
15
|
+
text: string;
|
|
16
|
+
/** Position strategy. Dynamic movement can make cropping less convenient. */
|
|
17
|
+
position?: "dynamic" | "top-left" | "top-right" | "bottom-left" | "bottom-right" | "center";
|
|
18
|
+
/** Reposition interval in seconds when position is dynamic (default: 15) */
|
|
19
|
+
intervalSeconds?: number;
|
|
20
|
+
/** Text opacity from 0.0 to 1.0 (default: 0.35) */
|
|
21
|
+
opacity?: number;
|
|
22
|
+
/** Font size (e.g. "12px", 12) */
|
|
23
|
+
fontSize?: number | string;
|
|
24
|
+
/** Text color (default: "#ffffff") */
|
|
25
|
+
color?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface VexorVanillaPlayerOptions {
|
|
29
|
+
src?: string;
|
|
30
|
+
assetId?: string;
|
|
31
|
+
token?: string;
|
|
32
|
+
baseUrl?: string;
|
|
33
|
+
poster?: string;
|
|
34
|
+
autoPlay?: boolean;
|
|
35
|
+
muted?: boolean;
|
|
36
|
+
loop?: boolean;
|
|
37
|
+
controls?: boolean;
|
|
38
|
+
preload?: "auto" | "metadata" | "none";
|
|
39
|
+
fallbackSrc?: string;
|
|
40
|
+
watermark?: string | WatermarkConfig;
|
|
41
|
+
/** Casual-copy deterrent only; does not prevent capture or guarantee access control. */
|
|
42
|
+
protect?: boolean;
|
|
43
|
+
refreshToken?: () => Promise<string>;
|
|
44
|
+
onReady?: () => void;
|
|
45
|
+
onPlay?: () => void;
|
|
46
|
+
onPause?: () => void;
|
|
47
|
+
onEnded?: () => void;
|
|
48
|
+
onTimeUpdate?: (currentTime: number, duration: number) => void;
|
|
49
|
+
onError?: (error: {
|
|
50
|
+
code: string;
|
|
51
|
+
message: string;
|
|
52
|
+
fatal: boolean;
|
|
53
|
+
}) => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface VanillaPlayerInstance {
|
|
57
|
+
play: () => Promise<void>;
|
|
58
|
+
pause: () => void;
|
|
59
|
+
seek: (seconds: number) => void;
|
|
60
|
+
destroy: () => void;
|
|
61
|
+
getElement: () => HTMLVideoElement;
|
|
62
|
+
}
|
|
63
|
+
/** Mount the Vexor player into an element without requiring React. */
|
|
64
|
+
declare function createVexorPlayer(target: HTMLElement | string, options: VexorVanillaPlayerOptions): VanillaPlayerInstance;
|
|
65
|
+
|
|
66
|
+
export { type QualityLevel as Q, type VanillaPlayerInstance as V, type WatermarkConfig as W, type VexorVanillaPlayerOptions as a, createVexorPlayer as c };
|
package/dist/vanilla.cjs
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/vanilla.ts
|
|
31
|
+
var vanilla_exports = {};
|
|
32
|
+
__export(vanilla_exports, {
|
|
33
|
+
createVexorPlayer: () => createVexorPlayer
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(vanilla_exports);
|
|
36
|
+
|
|
37
|
+
// src/vanilla/player.ts
|
|
38
|
+
var import_hls = __toESM(require("hls.js"), 1);
|
|
39
|
+
|
|
40
|
+
// src/native-token-refresh.ts
|
|
41
|
+
var TOKEN_REFRESH_INTERVAL_MS = 4 * 60 * 1e3;
|
|
42
|
+
var TOKEN_REFRESH_RETRY_MS = 30 * 1e3;
|
|
43
|
+
function startNativeTokenRefresh(video, initialUrl, refreshToken) {
|
|
44
|
+
let currentUrl = initialUrl;
|
|
45
|
+
let disposed = false;
|
|
46
|
+
let timer;
|
|
47
|
+
let refreshInFlight = null;
|
|
48
|
+
let restoreListener;
|
|
49
|
+
const schedule = (delay = TOKEN_REFRESH_INTERVAL_MS) => {
|
|
50
|
+
if (!refreshToken || disposed) return;
|
|
51
|
+
if (timer) clearTimeout(timer);
|
|
52
|
+
timer = setTimeout(() => void refreshNow(), delay);
|
|
53
|
+
};
|
|
54
|
+
const refreshNow = () => {
|
|
55
|
+
if (!refreshToken || disposed) return Promise.resolve(false);
|
|
56
|
+
if (refreshInFlight) return refreshInFlight;
|
|
57
|
+
refreshInFlight = (async () => {
|
|
58
|
+
try {
|
|
59
|
+
const token = await refreshToken();
|
|
60
|
+
if (disposed || !token) return false;
|
|
61
|
+
const nextUrl = new URL(currentUrl, document.baseURI);
|
|
62
|
+
nextUrl.searchParams.set("token", token);
|
|
63
|
+
currentUrl = nextUrl.toString();
|
|
64
|
+
const resumeTime = video.currentTime;
|
|
65
|
+
const shouldResume = !video.paused;
|
|
66
|
+
restoreListener = () => {
|
|
67
|
+
restoreListener = void 0;
|
|
68
|
+
if (disposed) return;
|
|
69
|
+
if (Number.isFinite(resumeTime) && resumeTime > 0) {
|
|
70
|
+
try {
|
|
71
|
+
video.currentTime = resumeTime;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (shouldResume) video.play().catch(() => {
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
video.addEventListener("loadedmetadata", restoreListener, { once: true });
|
|
79
|
+
video.src = currentUrl;
|
|
80
|
+
video.load();
|
|
81
|
+
schedule();
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
schedule(TOKEN_REFRESH_RETRY_MS);
|
|
85
|
+
return false;
|
|
86
|
+
} finally {
|
|
87
|
+
refreshInFlight = null;
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
return refreshInFlight;
|
|
91
|
+
};
|
|
92
|
+
schedule();
|
|
93
|
+
return {
|
|
94
|
+
refreshNow,
|
|
95
|
+
stop: () => {
|
|
96
|
+
disposed = true;
|
|
97
|
+
if (timer) clearTimeout(timer);
|
|
98
|
+
if (restoreListener) video.removeEventListener("loadedmetadata", restoreListener);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/vanilla/player.ts
|
|
104
|
+
function createVexorPlayer(target, options) {
|
|
105
|
+
const container = typeof target === "string" ? document.querySelector(target) : target;
|
|
106
|
+
if (!container) throw new Error(`Target container not found: ${String(target)}`);
|
|
107
|
+
const initialUrl = options.src ?? (options.assetId ? `${(options.baseUrl || "https://api.vexor.et").replace(/\/+$/, "")}/v1/playback/${encodeURIComponent(options.assetId)}/master.m3u8${options.token ? `?token=${encodeURIComponent(options.token)}` : ""}` : "");
|
|
108
|
+
if (!initialUrl) throw new Error("Provide either src or assetId to create the Vexor player.");
|
|
109
|
+
const hadPlayerClass = container.classList.contains("vexor-player");
|
|
110
|
+
const hadProtectedClass = container.classList.contains("is-protected");
|
|
111
|
+
container.classList.add("vexor-player");
|
|
112
|
+
const video = document.createElement("video");
|
|
113
|
+
video.className = "vexor-video";
|
|
114
|
+
video.controls = options.controls ?? true;
|
|
115
|
+
video.playsInline = true;
|
|
116
|
+
video.preload = options.preload ?? "metadata";
|
|
117
|
+
if (options.poster) video.poster = options.poster;
|
|
118
|
+
if (options.autoPlay) video.autoplay = true;
|
|
119
|
+
if (options.muted) video.muted = true;
|
|
120
|
+
if (options.loop) video.loop = true;
|
|
121
|
+
container.appendChild(video);
|
|
122
|
+
let watermarkElement = null;
|
|
123
|
+
let watermarkTimer;
|
|
124
|
+
if (options.watermark) {
|
|
125
|
+
const watermark = typeof options.watermark === "string" ? { text: options.watermark, position: "dynamic", intervalSeconds: 15, opacity: 0.35 } : { position: "dynamic", intervalSeconds: 15, opacity: 0.35, ...options.watermark };
|
|
126
|
+
watermarkElement = document.createElement("div");
|
|
127
|
+
watermarkElement.className = "vexor-watermark";
|
|
128
|
+
watermarkElement.textContent = watermark.text;
|
|
129
|
+
watermarkElement.style.opacity = String(Math.max(0, Math.min(1, watermark.opacity ?? 0.35)));
|
|
130
|
+
watermarkElement.style.fontSize = typeof watermark.fontSize === "number" ? `${watermark.fontSize}px` : watermark.fontSize ?? "12px";
|
|
131
|
+
watermarkElement.style.color = watermark.color ?? "#ffffff";
|
|
132
|
+
const placeWatermark = () => {
|
|
133
|
+
watermarkElement.style.removeProperty("top");
|
|
134
|
+
watermarkElement.style.removeProperty("right");
|
|
135
|
+
watermarkElement.style.removeProperty("bottom");
|
|
136
|
+
watermarkElement.style.removeProperty("left");
|
|
137
|
+
watermarkElement.style.removeProperty("transform");
|
|
138
|
+
switch (watermark.position) {
|
|
139
|
+
case "top-left":
|
|
140
|
+
watermarkElement.style.cssText += ";top:16px;left:16px";
|
|
141
|
+
break;
|
|
142
|
+
case "top-right":
|
|
143
|
+
watermarkElement.style.cssText += ";top:16px;right:16px";
|
|
144
|
+
break;
|
|
145
|
+
case "bottom-left":
|
|
146
|
+
watermarkElement.style.cssText += ";bottom:48px;left:16px";
|
|
147
|
+
break;
|
|
148
|
+
case "bottom-right":
|
|
149
|
+
watermarkElement.style.cssText += ";bottom:48px;right:16px";
|
|
150
|
+
break;
|
|
151
|
+
case "center":
|
|
152
|
+
watermarkElement.style.cssText += ";top:50%;left:50%;transform:translate(-50%,-50%)";
|
|
153
|
+
break;
|
|
154
|
+
default:
|
|
155
|
+
watermarkElement.style.top = `${10 + Math.floor(Math.random() * 70)}%`;
|
|
156
|
+
watermarkElement.style.left = `${10 + Math.floor(Math.random() * 70)}%`;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
placeWatermark();
|
|
160
|
+
if (watermark.position === "dynamic") {
|
|
161
|
+
watermarkTimer = setInterval(placeWatermark, Math.max(2, watermark.intervalSeconds || 15) * 1e3);
|
|
162
|
+
}
|
|
163
|
+
container.appendChild(watermarkElement);
|
|
164
|
+
}
|
|
165
|
+
const onContextMenu = (event) => event.preventDefault();
|
|
166
|
+
const onDragStart = (event) => event.preventDefault();
|
|
167
|
+
if (options.protect) {
|
|
168
|
+
container.classList.add("is-protected");
|
|
169
|
+
container.addEventListener("contextmenu", onContextMenu);
|
|
170
|
+
video.addEventListener("contextmenu", onContextMenu);
|
|
171
|
+
video.addEventListener("dragstart", onDragStart);
|
|
172
|
+
}
|
|
173
|
+
let activeUrl = initialUrl;
|
|
174
|
+
let hls = null;
|
|
175
|
+
let nativeTokenRefresh = null;
|
|
176
|
+
let nativeErrorListener;
|
|
177
|
+
let destroyed = false;
|
|
178
|
+
let refreshPromise = null;
|
|
179
|
+
let resumeAfterRefresh = null;
|
|
180
|
+
let hasSignaledReady = false;
|
|
181
|
+
let activeParams = new URL(activeUrl, document.baseURI).search;
|
|
182
|
+
const useFallback = (error) => {
|
|
183
|
+
if (options.fallbackSrc) {
|
|
184
|
+
if (hls) hls.destroy();
|
|
185
|
+
hls = null;
|
|
186
|
+
video.src = options.fallbackSrc;
|
|
187
|
+
video.load();
|
|
188
|
+
} else if (error) options.onError?.(error);
|
|
189
|
+
};
|
|
190
|
+
const signalReady = () => {
|
|
191
|
+
if (hasSignaledReady) return;
|
|
192
|
+
hasSignaledReady = true;
|
|
193
|
+
options.onReady?.();
|
|
194
|
+
};
|
|
195
|
+
const onLoaded = () => {
|
|
196
|
+
signalReady();
|
|
197
|
+
const resume = resumeAfterRefresh;
|
|
198
|
+
resumeAfterRefresh = null;
|
|
199
|
+
if (resume && Number.isFinite(resume.time)) video.currentTime = resume.time;
|
|
200
|
+
if ((resume?.shouldPlay || options.autoPlay) && !destroyed) video.play().catch(() => {
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
video.addEventListener("loadedmetadata", onLoaded);
|
|
204
|
+
video.addEventListener("loadeddata", onLoaded);
|
|
205
|
+
const onPlay = () => options.onPlay?.();
|
|
206
|
+
const onPause = () => options.onPause?.();
|
|
207
|
+
const onEnded = () => options.onEnded?.();
|
|
208
|
+
video.addEventListener("play", onPlay);
|
|
209
|
+
video.addEventListener("pause", onPause);
|
|
210
|
+
video.addEventListener("ended", onEnded);
|
|
211
|
+
const onTimeUpdate = () => options.onTimeUpdate?.(video.currentTime, video.duration);
|
|
212
|
+
video.addEventListener("timeupdate", onTimeUpdate);
|
|
213
|
+
const isHlsUrl = activeUrl.includes(".m3u8") || activeUrl.includes("/playback/");
|
|
214
|
+
if (isHlsUrl && import_hls.default.isSupported()) {
|
|
215
|
+
class TokenAwareLoader extends import_hls.default.DefaultConfig.loader {
|
|
216
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
217
|
+
load(context, config, callbacks) {
|
|
218
|
+
if (activeParams && context?.url) {
|
|
219
|
+
try {
|
|
220
|
+
const sourceUrl = new URL(activeUrl, document.baseURI);
|
|
221
|
+
const childUrl = new URL(context.url, sourceUrl);
|
|
222
|
+
if (childUrl.origin === sourceUrl.origin && !childUrl.searchParams.has("token")) {
|
|
223
|
+
new URLSearchParams(activeParams).forEach((value, key) => {
|
|
224
|
+
if (!childUrl.searchParams.has(key)) childUrl.searchParams.set(key, value);
|
|
225
|
+
});
|
|
226
|
+
context.url = childUrl.toString();
|
|
227
|
+
}
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
super.load(context, config, callbacks);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
hls = new import_hls.default({ enableWorker: true, loader: TokenAwareLoader, fLoader: TokenAwareLoader, pLoader: TokenAwareLoader });
|
|
235
|
+
const activeHls = hls;
|
|
236
|
+
activeHls.on(import_hls.default.Events.MANIFEST_PARSED, () => {
|
|
237
|
+
if (hls !== activeHls || destroyed) return;
|
|
238
|
+
const resume = resumeAfterRefresh;
|
|
239
|
+
resumeAfterRefresh = null;
|
|
240
|
+
signalReady();
|
|
241
|
+
if (resume && Number.isFinite(resume.time)) video.currentTime = resume.time;
|
|
242
|
+
if ((resume?.shouldPlay || options.autoPlay) && !destroyed) video.play().catch(() => {
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
activeHls.on(import_hls.default.Events.ERROR, async (_event, data) => {
|
|
246
|
+
if (hls !== activeHls || destroyed) return;
|
|
247
|
+
if ((data.response?.code === 401 || data.response?.code === 403) && options.refreshToken) {
|
|
248
|
+
try {
|
|
249
|
+
refreshPromise ??= options.refreshToken();
|
|
250
|
+
const newToken = await refreshPromise;
|
|
251
|
+
refreshPromise = null;
|
|
252
|
+
if (newToken && hls === activeHls && !destroyed) {
|
|
253
|
+
resumeAfterRefresh = { time: video.currentTime, shouldPlay: !video.paused };
|
|
254
|
+
const url = new URL(activeUrl, document.baseURI);
|
|
255
|
+
url.searchParams.set("token", newToken);
|
|
256
|
+
activeUrl = url.toString();
|
|
257
|
+
activeParams = url.search;
|
|
258
|
+
activeHls.loadSource(activeUrl);
|
|
259
|
+
activeHls.startLoad();
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
} catch {
|
|
263
|
+
refreshPromise = null;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (hls !== activeHls || destroyed) return;
|
|
267
|
+
if (data.fatal) useFallback({ code: data.details || "MEDIA_PLAYBACK_ERROR", message: `Failed to stream video: ${data.details || "network error"}`, fatal: true });
|
|
268
|
+
});
|
|
269
|
+
activeHls.loadSource(activeUrl);
|
|
270
|
+
activeHls.attachMedia(video);
|
|
271
|
+
} else if (isHlsUrl && video.canPlayType("application/vnd.apple.mpegurl")) {
|
|
272
|
+
video.src = activeUrl;
|
|
273
|
+
nativeTokenRefresh = startNativeTokenRefresh(video, activeUrl, options.refreshToken);
|
|
274
|
+
nativeErrorListener = () => {
|
|
275
|
+
void nativeTokenRefresh?.refreshNow().then((refreshed) => {
|
|
276
|
+
if (refreshed || destroyed) return;
|
|
277
|
+
video.removeEventListener("error", nativeErrorListener);
|
|
278
|
+
useFallback({ code: "NATIVE_HLS_ERROR", message: "Failed to play native HLS stream", fatal: true });
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
video.addEventListener("error", nativeErrorListener);
|
|
282
|
+
} else if (!isHlsUrl || options.fallbackSrc) {
|
|
283
|
+
video.src = isHlsUrl ? options.fallbackSrc : activeUrl;
|
|
284
|
+
} else {
|
|
285
|
+
useFallback({ code: "HLS_UNSUPPORTED", message: "This browser does not support HLS playback.", fatal: true });
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
play: () => video.play(),
|
|
289
|
+
pause: () => video.pause(),
|
|
290
|
+
seek: (seconds) => {
|
|
291
|
+
video.currentTime = Math.max(0, Math.min(seconds, video.duration || seconds));
|
|
292
|
+
},
|
|
293
|
+
destroy: () => {
|
|
294
|
+
if (destroyed) return;
|
|
295
|
+
destroyed = true;
|
|
296
|
+
nativeTokenRefresh?.stop();
|
|
297
|
+
if (hls) hls.destroy();
|
|
298
|
+
hls = null;
|
|
299
|
+
if (watermarkTimer) clearInterval(watermarkTimer);
|
|
300
|
+
container.removeEventListener("contextmenu", onContextMenu);
|
|
301
|
+
video.removeEventListener("contextmenu", onContextMenu);
|
|
302
|
+
video.removeEventListener("dragstart", onDragStart);
|
|
303
|
+
video.removeEventListener("loadedmetadata", onLoaded);
|
|
304
|
+
video.removeEventListener("loadeddata", onLoaded);
|
|
305
|
+
video.removeEventListener("play", onPlay);
|
|
306
|
+
video.removeEventListener("pause", onPause);
|
|
307
|
+
video.removeEventListener("ended", onEnded);
|
|
308
|
+
video.removeEventListener("timeupdate", onTimeUpdate);
|
|
309
|
+
if (nativeErrorListener) video.removeEventListener("error", nativeErrorListener);
|
|
310
|
+
video.pause();
|
|
311
|
+
video.removeAttribute("src");
|
|
312
|
+
video.load();
|
|
313
|
+
video.remove();
|
|
314
|
+
watermarkElement?.remove();
|
|
315
|
+
if (!hadPlayerClass) container.classList.remove("vexor-player");
|
|
316
|
+
if (options.protect && !hadProtectedClass) container.classList.remove("is-protected");
|
|
317
|
+
},
|
|
318
|
+
getElement: () => video
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
322
|
+
0 && (module.exports = {
|
|
323
|
+
createVexorPlayer
|
|
324
|
+
});
|
|
325
|
+
//# sourceMappingURL=vanilla.cjs.map
|