@openrails/contentkit-ui 0.90.3 → 0.92.1
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/README.md +6 -4
- package/dist/client/client.d.ts +1 -1
- package/dist/client/client.js.map +1 -1
- package/dist/client/generated/routes.d.ts +7 -7
- package/dist/client/generated/routes.js +6 -6
- package/dist/client/generated/routes.js.map +1 -1
- package/dist/client/http.d.ts +13 -8
- package/dist/client/http.js +16 -4
- package/dist/client/http.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/media/client.d.ts +3 -3
- package/dist/client/media/client.js +2 -2
- package/dist/client/media/client.js.map +1 -1
- package/dist/components/hover-preview.js +1 -1
- package/dist/components/hover-preview.js.map +1 -1
- package/dist/components/media-gallery.d.ts +2 -2
- package/dist/components/media-gallery.js +4 -4
- package/dist/components/media-gallery.js.map +1 -1
- package/dist/components/video-mini-player.js +1 -1
- package/dist/components/video-mini-player.js.map +1 -1
- package/dist/components/video-player.d.ts +3 -3
- package/dist/components/video-player.js +3 -3
- package/dist/components/video-player.js.map +1 -1
- package/dist/react/gallery.d.ts +6 -3
- package/dist/react/gallery.js +303 -228
- package/dist/react/gallery.js.map +1 -1
- package/dist/react/player.d.ts +4 -6
- package/dist/react/player.js +54 -61
- package/dist/react/player.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-mini-player.js","names":[],"sources":["../../src/components/video-mini-player.tsx"],"sourcesContent":["import { Cancel01Icon, GoBackward10SecIcon, GoForward10SecIcon, SquareArrowExpand01Icon, PauseIcon, PlayIcon, VolumeHighIcon, VolumeOffIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { JUMP_SEEK, PLAYER_VOLUME_KEY, playerKeyAction, writeVolume, type PlayerAction } from \"../client/player.js\";\nimport { publicRenditions } from \"../client/public.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useActiveCues, useMediaState, useProgressReports, type PlaybackProgress } from \"../react/player.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { Captions, ControlButton, SeekBar, Spinner, clock } from \"./player-controls.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { SpriteFrame, keyAllowed, type PlayerHandoff } from \"./video-player.js\";\n\nconst CONTROLS_IDLE_MS = 2500;\n\nexport interface VideoMiniPlayerProps extends Omit<HlsPlayerOptions, \"src\" | \"startAt\" | \"autoPlay\" | \"audioLanguage\" | \"subtitleLanguage\" | \"active\"> {\n /** What VideoPlayer's onMiniPlayer handed over. */\n handoff: PlayerHandoff;\n /** Back to the full player, where this one is. */\n onExpand: (state: { time: number; playing: boolean }) => void;\n onClose: (state: { time: number }) => void;\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n /** K, J, L and M page-wide (default), every shortcut while focus is in it, or none. */\n keyboard?: \"global\" | \"focus\" | false;\n /** The video's title. */\n label?: string;\n position?: \"bottom-right\" | \"bottom-left\";\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\n/**\n * The video docked in a corner while the viewer browses: play, ±10 s, mute,\n * a seek line, expand back to the full player and close. It starts where\n * the handoff stopped, playing if it was.\n */\nexport function VideoMiniPlayer({\n handoff,\n onExpand,\n onClose,\n onProgress,\n progressInterval = 10_000,\n keyboard = \"global\",\n label,\n position = \"bottom-right\",\n crossOrigin,\n className,\n style,\n appearance,\n ...options\n}: VideoMiniPlayerProps) {\n const { t } = useMessages();\n const src = `${handoff.base}master.m3u8`;\n const player = useHlsPlayer({\n ...options,\n src,\n startAt: handoff.time,\n autoPlay: handoff.playing,\n audioLanguage: handoff.audioLanguage,\n subtitleLanguage: handoff.subtitleLanguage,\n // The handoff carries the viewer's choices; a few hundred pixels need no manual rung.\n trackKey: null,\n qualityKey: null,\n });\n const { status, error, started } = player;\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n const media = useMediaState(video);\n const cues = useActiveCues(video);\n useProgressReports(video, { enabled: started, source: started ? src : null, interval: progressInterval, onProgress });\n\n const [poked, setPoked] = useState(true);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n const [scrubbing, setScrubbing] = useState(false);\n const playing = !media.paused && !media.ended;\n const shown = poked || !playing || scrubbing || !!error;\n\n const done = useRef(false);\n const time = () => video?.currentTime || media.time || handoff.time;\n const expand = () => {\n if (done.current) return;\n done.current = true;\n onExpand({ time: time(), playing });\n };\n const close = () => {\n if (done.current) return;\n done.current = true;\n video?.pause();\n onClose({ time: time() });\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.paused || video.ended) player.play();\n else video.pause();\n };\n const dur = media.duration || handoff.duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n };\n\n const run = (a: PlayerAction, key: string): boolean => {\n if (!video) return false;\n // Page-wide, only K, J, L and M: Space and the arrows scroll the page.\n if (keyboard === \"global\" && !\"kjlmKJLM\".includes(key)) return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n if (!started) return false;\n seek(video.currentTime + a.seconds);\n break;\n case \"mute\":\n toggleMute();\n break;\n default:\n return false;\n }\n poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n useEffect(() => {\n if (!keyboard || !root) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action, k.key)) return;\n k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n return () => on.removeEventListener(\"keydown\", down);\n }, [keyboard, root]);\n\n // Touch has no hover: a tap shows or hides the controls. A click plays and pauses.\n const pointerType = useRef(\"mouse\");\n const onSurfaceClick = (e: MouseEvent) => {\n e.preventDefault();\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n } else if (shown && playing) hide();\n else poke();\n };\n\n const aspect = handoff.width && handoff.height ? handoff.width / handoff.height : (media.aspect ?? 16 / 9);\n const posterOutputs = typeof handoff.poster === \"string\" ? [] : publicRenditions(handoff.poster);\n const busy = !error && (status === \"loading\" || status === \"buffering\");\n const overlay = \"rounded-full text-white hover:bg-white/15\";\n return (\n <ContentKitUiRoot\n ref={setRoot}\n appearance={appearance}\n role=\"region\"\n aria-label={label ? `${t(\"player.miniPlayer\")}: ${label}` : t(\"player.miniPlayer\")}\n data-ckui=\"mini-player\"\n data-status={status}\n className={cn(\n \"@container fixed bottom-4 z-50 touch-manipulation overflow-hidden rounded-md bg-black text-white shadow-lg select-none\",\n position === \"bottom-left\" ? \"left-4\" : \"right-4\",\n className,\n )}\n style={{ aspectRatio: String(aspect), width: `min(24rem, calc(100vw - 2rem), calc(60svh * ${aspect}))`, ...style }}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && playing && hide()}\n >\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {!started &&\n (typeof handoff.poster === \"string\" ? (\n <img alt=\"\" src={handoff.poster} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : (\n <SpriteFrame vtt={`${handoff.base}sprite.vtt`} xhrSetup={options.xhrSetup} />\n ))}\n <div aria-hidden data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} />\n <Captions cues={started ? cues : []} bottom={shown ? \"2.75rem\" : \"0.75rem\"} />\n <div\n data-ckui=\"controls\"\n className={cn(\n \"pointer-events-none absolute inset-0 bg-black/45 transition-opacity duration-200 focus-within:opacity-100 motion-reduce:transition-none\",\n shown ? \"opacity-100 [&_button]:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <div className=\"absolute inset-x-0 top-0 flex items-center justify-between p-1.5\">\n <ControlButton label={t(\"player.expand\")} icon={SquareArrowExpand01Icon} onClick={expand} className={overlay} data-ckui=\"mini-expand\" />\n <ControlButton label={t(\"player.close\")} icon={Cancel01Icon} onClick={close} className={overlay} data-ckui=\"mini-close\" />\n </div>\n <div className=\"absolute inset-0 m-auto flex h-fit w-fit items-center gap-3 @sm:gap-5\">\n <ControlButton\n label={t(\"player.seekBack\", { seconds: JUMP_SEEK })}\n icon={GoBackward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() - JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n <button\n type=\"button\"\n aria-label={playing ? t(\"player.pause\") : t(\"player.resume\")}\n title={playing ? t(\"player.pause\") : t(\"player.resume\")}\n onClick={() => {\n toggle();\n poke();\n }}\n className={cn(\"flex size-12 cursor-pointer items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-white/80\", overlay)}\n data-ckui=\"mini-play\"\n >\n {busy && started ? <Spinner className=\"size-7\" /> : <HugeiconsIcon icon={playing ? PauseIcon : PlayIcon} className=\"size-7 fill-current\" strokeWidth={1.5} aria-hidden />}\n </button>\n <ControlButton\n label={t(\"player.seekForward\", { seconds: JUMP_SEEK })}\n icon={GoForward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() + JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n </div>\n <div className=\"absolute inset-x-0 bottom-3 flex items-center justify-between px-2.5\">\n <span className=\"text-xs font-medium tabular-nums\">{dur > 0 ? `${clock(media.time || handoff.time)} / ${clock(dur)}` : clock(media.time || handoff.time)}</span>\n <ControlButton label={media.muted ? t(\"player.unmute\") : t(\"player.mute\")} icon={media.muted ? VolumeOffIcon : VolumeHighIcon} onClick={toggleMute} className={cn(overlay, \"size-8\")} />\n </div>\n </div>\n <div className=\"absolute inset-x-0 bottom-0 z-10\">\n <SeekBar thin time={media.time || handoff.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} />\n </div>\n {error && (\n <div role=\"alert\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 p-3 text-center text-xs\">\n <p>{t(`player.errors.${error.kind}`)}</p>\n <button type=\"button\" onClick={player.retry} className=\"rounded-md bg-white/15 px-2 py-1 font-medium hover:bg-white/25\">\n {t(\"player.retry\")}\n </button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video-mini-player.js","names":[],"sources":["../../src/components/video-mini-player.tsx"],"sourcesContent":["import { Cancel01Icon, GoBackward10SecIcon, GoForward10SecIcon, SquareArrowExpand01Icon, PauseIcon, PlayIcon, VolumeHighIcon, VolumeOffIcon } from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { JUMP_SEEK, PLAYER_VOLUME_KEY, playerKeyAction, writeVolume, type PlayerAction } from \"../client/player.js\";\nimport { publicRenditions } from \"../client/public.js\";\nimport { useMessages } from \"../i18n/context.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useActiveCues, useMediaState, useProgressReports, type PlaybackProgress } from \"../react/player.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { Captions, ControlButton, SeekBar, Spinner, clock } from \"./player-controls.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { SpriteFrame, keyAllowed, type PlayerHandoff } from \"./video-player.js\";\n\nconst CONTROLS_IDLE_MS = 2500;\n\nexport interface VideoMiniPlayerProps extends Omit<HlsPlayerOptions, \"src\" | \"startAt\" | \"autoPlay\" | \"audioLanguage\" | \"subtitleLanguage\" | \"active\"> {\n /** What VideoPlayer's onMiniPlayer handed over. */\n handoff: PlayerHandoff;\n /** Back to the full player, where this one is. */\n onExpand: (state: { time: number; playing: boolean }) => void;\n onClose: (state: { time: number }) => void;\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n /** K, J, L and M page-wide (default), every shortcut while focus is in it, or none. */\n keyboard?: \"global\" | \"focus\" | false;\n /** The video's title. */\n label?: string;\n position?: \"bottom-right\" | \"bottom-left\";\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\n/**\n * The video docked in a corner while the viewer browses: play, ±10 s, mute,\n * a seek line, expand back to the full player and close. It starts where\n * the handoff stopped, playing if it was.\n */\nexport function VideoMiniPlayer({\n handoff,\n onExpand,\n onClose,\n onProgress,\n progressInterval = 10_000,\n keyboard = \"global\",\n label,\n position = \"bottom-right\",\n crossOrigin,\n className,\n style,\n appearance,\n ...options\n}: VideoMiniPlayerProps) {\n const { t } = useMessages();\n const src = `${handoff.base}master.m3u8`;\n const player = useHlsPlayer({\n ...options,\n src,\n startAt: handoff.time,\n autoPlay: handoff.playing,\n audioLanguage: handoff.audioLanguage,\n subtitleLanguage: handoff.subtitleLanguage,\n // The handoff carries the viewer's choices; a few hundred pixels need no manual rung.\n trackKey: null,\n qualityKey: null,\n });\n const { status, error, started } = player;\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n const media = useMediaState(video);\n const cues = useActiveCues(video);\n useProgressReports(video, { enabled: started, source: started ? src : null, interval: progressInterval, onProgress });\n\n const [poked, setPoked] = useState(true);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n const [scrubbing, setScrubbing] = useState(false);\n const playing = !media.paused && !media.ended;\n const shown = poked || !playing || scrubbing || !!error;\n\n const done = useRef(false);\n const time = () => video?.currentTime || media.time || handoff.time;\n const expand = () => {\n if (done.current) return;\n done.current = true;\n onExpand({ time: time(), playing });\n };\n const close = () => {\n if (done.current) return;\n done.current = true;\n video?.pause();\n onClose({ time: time() });\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.paused || video.ended) player.play();\n else video.pause();\n };\n const dur = media.duration || handoff.duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n };\n\n const run = (a: PlayerAction, key: string): boolean => {\n if (!video) return false;\n // Page-wide, only K, J, L and M: Space and the arrows scroll the page.\n if (keyboard === \"global\" && !\"kjlmKJLM\".includes(key)) return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n if (!started) return false;\n seek(video.currentTime + a.seconds);\n break;\n case \"mute\":\n toggleMute();\n break;\n default:\n return false;\n }\n poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n useEffect(() => {\n if (!keyboard || !root) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action, k.key)) return;\n k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n return () => on.removeEventListener(\"keydown\", down);\n }, [keyboard, root]);\n\n // Touch has no hover: a tap shows or hides the controls. A click plays and pauses.\n const pointerType = useRef(\"mouse\");\n const onSurfaceClick = (e: MouseEvent) => {\n e.preventDefault();\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n } else if (shown && playing) hide();\n else poke();\n };\n\n const aspect = handoff.width && handoff.height ? handoff.width / handoff.height : (media.aspect ?? 16 / 9);\n const posterOutputs = typeof handoff.poster === \"string\" ? [] : publicRenditions(handoff.poster);\n const busy = !error && (status === \"loading\" || status === \"buffering\");\n const overlay = \"rounded-full text-white hover:bg-white/15\";\n return (\n <ContentKitUiRoot\n ref={setRoot}\n appearance={appearance}\n role=\"region\"\n aria-label={label ? `${t(\"player.miniPlayer\")}: ${label}` : t(\"player.miniPlayer\")}\n data-ckui=\"mini-player\"\n data-status={status}\n className={cn(\n \"@container fixed bottom-4 z-50 touch-manipulation overflow-hidden rounded-md bg-black text-white shadow-lg select-none\",\n position === \"bottom-left\" ? \"left-4\" : \"right-4\",\n className,\n )}\n style={{ aspectRatio: String(aspect), width: `min(24rem, calc(100vw - 2rem), calc(60svh * ${aspect}))`, ...style }}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && playing && hide()}\n >\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {!started &&\n (typeof handoff.poster === \"string\" ? (\n <img alt=\"\" src={handoff.poster} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className=\"pointer-events-none absolute inset-0 size-full object-contain\" />\n ) : (\n <SpriteFrame vtt={`${handoff.base}sprite.vtt`} loader={options.loader} />\n ))}\n <div aria-hidden data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} />\n <Captions cues={started ? cues : []} bottom={shown ? \"2.75rem\" : \"0.75rem\"} />\n <div\n data-ckui=\"controls\"\n className={cn(\n \"pointer-events-none absolute inset-0 bg-black/45 transition-opacity duration-200 focus-within:opacity-100 motion-reduce:transition-none\",\n shown ? \"opacity-100 [&_button]:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <div className=\"absolute inset-x-0 top-0 flex items-center justify-between p-1.5\">\n <ControlButton label={t(\"player.expand\")} icon={SquareArrowExpand01Icon} onClick={expand} className={overlay} data-ckui=\"mini-expand\" />\n <ControlButton label={t(\"player.close\")} icon={Cancel01Icon} onClick={close} className={overlay} data-ckui=\"mini-close\" />\n </div>\n <div className=\"absolute inset-0 m-auto flex h-fit w-fit items-center gap-3 @sm:gap-5\">\n <ControlButton\n label={t(\"player.seekBack\", { seconds: JUMP_SEEK })}\n icon={GoBackward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() - JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n <button\n type=\"button\"\n aria-label={playing ? t(\"player.pause\") : t(\"player.resume\")}\n title={playing ? t(\"player.pause\") : t(\"player.resume\")}\n onClick={() => {\n toggle();\n poke();\n }}\n className={cn(\"flex size-12 cursor-pointer items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-white/80\", overlay)}\n data-ckui=\"mini-play\"\n >\n {busy && started ? <Spinner className=\"size-7\" /> : <HugeiconsIcon icon={playing ? PauseIcon : PlayIcon} className=\"size-7 fill-current\" strokeWidth={1.5} aria-hidden />}\n </button>\n <ControlButton\n label={t(\"player.seekForward\", { seconds: JUMP_SEEK })}\n icon={GoForward10SecIcon}\n disabled={!started}\n onClick={() => {\n seek(time() + JUMP_SEEK);\n poke();\n }}\n className={cn(overlay, \"[&_svg]:size-6\")}\n />\n </div>\n <div className=\"absolute inset-x-0 bottom-3 flex items-center justify-between px-2.5\">\n <span className=\"text-xs font-medium tabular-nums\">{dur > 0 ? `${clock(media.time || handoff.time)} / ${clock(dur)}` : clock(media.time || handoff.time)}</span>\n <ControlButton label={media.muted ? t(\"player.unmute\") : t(\"player.mute\")} icon={media.muted ? VolumeOffIcon : VolumeHighIcon} onClick={toggleMute} className={cn(overlay, \"size-8\")} />\n </div>\n </div>\n <div className=\"absolute inset-x-0 bottom-0 z-10\">\n <SeekBar thin time={media.time || handoff.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} />\n </div>\n {error && (\n <div role=\"alert\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-2 bg-black/80 p-3 text-center text-xs\">\n <p>{t(`player.errors.${error.kind}`)}</p>\n <button type=\"button\" onClick={player.retry} className=\"rounded-md bg-white/15 px-2 py-1 font-medium hover:bg-white/25\">\n {t(\"player.retry\")}\n </button>\n </div>\n )}\n </ContentKitUiRoot>\n );\n}\n"],"mappings":"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"}
|
|
@@ -6,11 +6,11 @@ import { HlsPlayerOptions } from '../react/gallery.js';
|
|
|
6
6
|
import { PlaybackProgress } from '../react/player.js';
|
|
7
7
|
import { PublicPreset } from '../client/public.js';
|
|
8
8
|
import { EncodeProgress as Progress } from '../client/generated/wire.js';
|
|
9
|
-
type
|
|
9
|
+
type Loader = HlsPlayerOptions["loader"];
|
|
10
10
|
/** The first frame of a video's seek sprite (sprite.vtt), fitted like object-fit. */
|
|
11
|
-
export declare function SpriteFrame({ vtt,
|
|
11
|
+
export declare function SpriteFrame({ vtt, loader, fit, className }: {
|
|
12
12
|
vtt: string;
|
|
13
|
-
|
|
13
|
+
loader?: Loader;
|
|
14
14
|
fit?: "contain" | "cover";
|
|
15
15
|
className?: string;
|
|
16
16
|
}): import('react').JSX.Element | null;
|
|
@@ -16,7 +16,7 @@ import { cn as m } from "cn";
|
|
|
16
16
|
import { AlertCircleIcon as h, ArrowShrink02Icon as Ce, ClosedCaptionIcon as we, FullScreenIcon as Te, PauseIcon as Ee, PictureInPicture01Icon as De, PictureInPictureExitIcon as Oe, PictureInPictureOnIcon as ke, PlayIcon as Ae, RectangularIcon as je, Refresh01Icon as Me, Settings01Icon as Ne } from "@hugeicons/core-free-icons";
|
|
17
17
|
import { HugeiconsIcon as g } from "@hugeicons/react";
|
|
18
18
|
//#region src/components/video-player.tsx
|
|
19
|
-
function Pe({ vtt: e,
|
|
19
|
+
function Pe({ vtt: e, loader: t, fit: n = "contain", className: r }) {
|
|
20
20
|
let i = me(e, t), a = i?.cues[0];
|
|
21
21
|
return !i || !a ? null : /* @__PURE__ */ f("svg", {
|
|
22
22
|
"aria-hidden": !0,
|
|
@@ -56,7 +56,7 @@ function b({ base: o, poster: h, width: _, height: v, duration: y, pending: b, p
|
|
|
56
56
|
start: () => j.preview(We(Xe, y)),
|
|
57
57
|
stop: j.unload
|
|
58
58
|
});
|
|
59
|
-
let V = de(B), H = ue(z, B), U = fe(B), mt = le(B), ht = me(P && o ? `${o}sprite.vtt` : null, D.
|
|
59
|
+
let V = de(B), H = ue(z, B), U = fe(B), mt = le(B), ht = me(P && o ? `${o}sprite.vtt` : null, D.loader), gt = !F && P;
|
|
60
60
|
pe(B, {
|
|
61
61
|
enabled: gt,
|
|
62
62
|
source: gt ? lt : null,
|
|
@@ -423,7 +423,7 @@ function b({ base: o, poster: h, width: _, height: v, duration: y, pending: b, p
|
|
|
423
423
|
className: m("pointer-events-none absolute inset-0 size-full object-contain", $)
|
|
424
424
|
}) : /* @__PURE__ */ f(Pe, {
|
|
425
425
|
vtt: `${o}sprite.vtt`,
|
|
426
|
-
|
|
426
|
+
loader: D.loader,
|
|
427
427
|
className: $
|
|
428
428
|
}),
|
|
429
429
|
/* @__PURE__ */ f(Ue, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-player.js","names":[],"sources":["../../src/components/video-player.tsx"],"sourcesContent":["import {\n AlertCircleIcon,\n ArrowShrink02Icon,\n ClosedCaptionIcon,\n FullScreenIcon,\n PauseIcon,\n PictureInPicture01Icon,\n PictureInPictureExitIcon,\n PictureInPictureOnIcon,\n PlayIcon,\n RectangularIcon,\n Refresh01Icon,\n Settings01Icon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { formatDuration } from \"../client/gallery.js\";\nimport type { PlaybackError } from \"../client/playback.js\";\nimport {\n JUMP_SEEK,\n PLAYBACK_SPEEDS,\n PLAYER_VOLUME_KEY,\n nextSpeed,\n playerKeyAction,\n resumeAt,\n writeVolume,\n type PlayerAction,\n type PlayerTrack,\n} from \"../client/player.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\nimport { useActiveCues, useFullscreen, useMediaState, usePictureInPicture, useProgressReports, useSprite, type PlaybackProgress } from \"../react/player.js\";\nimport { useLanguageName, useMessages } from \"../i18n/context.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { publicRenditions, type PublicPreset } from \"../client/public.js\";\nimport type { EncodeProgress as Progress } from \"../client/generated/wire.js\";\nimport { EncodeProgress } from \"./encode-progress.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Captions, ControlButton, SeekBar, SeekFlash, SettingsMenu, Spinner, VolumeControl, clock, playerButtonClass, type MenuSection, type SeekFlashState } from \"./player-controls.js\";\n\ntype XhrSetup = HlsPlayerOptions[\"xhrSetup\"];\n\n/** The first frame of a video's seek sprite (sprite.vtt), fitted like object-fit. */\nexport function SpriteFrame({ vtt, xhrSetup, fit = \"contain\", className }: { vtt: string; xhrSetup?: XhrSetup; fit?: \"contain\" | \"cover\"; className?: string }) {\n const sprite = useSprite(vtt, xhrSetup);\n const cue = sprite?.cues[0];\n if (!sprite || !cue) return null;\n return (\n <svg\n aria-hidden\n className={cn(\"pointer-events-none absolute inset-0 size-full\", className)}\n viewBox={`${cue.x} ${cue.y} ${cue.w} ${cue.h}`}\n preserveAspectRatio={fit === \"cover\" ? \"xMidYMid slice\" : \"xMidYMid meet\"}\n data-ckui=\"sprite-frame\"\n >\n <image href={cue.url} width={sprite.width} height={sprite.height} />\n </svg>\n );\n}\n\ntype At<T> = T extends unknown ? T & { time: number } : never;\n\n/** Playback as it happens, for analytics; never during an inline preview. */\nexport type PlayerEvent = At<\n | { type: \"play\" | \"playing\" | \"pause\" | \"waiting\" | \"ended\" }\n | { type: \"seek\"; from: number }\n | { type: \"rate\"; rate: number }\n | { type: \"volume\"; volume: number; muted: boolean }\n /** The viewer's quality choice; height null is Auto. */\n | { type: \"quality\"; height: number | null }\n | { type: \"audio\"; track: PlayerTrack }\n | { type: \"subtitles\"; track: PlayerTrack | null }\n | { type: \"fullscreen\" | \"pip\" | \"theater\"; on: boolean }\n | { type: \"error\"; error: PlaybackError }\n>;\n\n/** Everything a VideoMiniPlayer needs to carry on where the player stopped. */\nexport interface PlayerHandoff {\n base: string;\n time: number;\n playing: boolean;\n width?: number;\n height?: number;\n duration?: number;\n poster?: string | PublicPreset | null;\n audioLanguage?: string;\n subtitleLanguage?: string | null;\n}\n\n/** A host entry in the settings menu: a choice (e.g. version) or an action (e.g. report a problem). */\nexport type PlayerMenuItem =\n | { label: string; value: string; options: readonly { value: string; label: string }[]; onChange: (value: string) => void }\n | { label: string; onSelect: () => void };\n\nexport interface PlayerSlotContext {\n /** Where popups must portal to stay visible: the player while fullscreen, else undefined (the body). */\n container?: HTMLElement;\n /** The control buttons' class. */\n className: string;\n}\n\nexport interface VideoPlayerProps extends Omit<HlsPlayerOptions, \"src\"> {\n /** The ladder's HLS folder (client.media.hlsBase: {media}/{kind}/{id}/hls/{dir}): master.m3u8 and sprite.vtt resolve under it. */\n base?: string | null;\n /** Shown until playback starts: the poster's public preset or a URL; default the first sprite frame. */\n poster?: string | PublicPreset | null;\n /** The video's size (read API w/h): the box is reserved before anything loads; default the decoded frame's. */\n width?: number;\n height?: number;\n duration?: number;\n /** A pending encode (an editor read's `pending` and `progress`): shows its progress instead of a player. */\n pending?: boolean;\n progress?: Progress | null;\n /** Why the video cannot be encoded (an editor read's `failed.message`). */\n failed?: string;\n /**\n * `frame` is as wide as its column allows at the video's aspect, never\n * taller than `maxHeight`; `fill` fills a sized parent (a carousel slide).\n */\n layout?: \"frame\" | \"fill\";\n maxHeight?: string;\n /** Preview muted inline before play (hover, or in view on touch); default the provider's. */\n inlinePreview?: boolean;\n /** Where the preview starts, seconds; default 10% in. */\n previewStart?: number;\n /** Shortcuts while focus is in the player (default), page-wide (a watch page: one such player per page), or none. */\n keyboard?: \"focus\" | \"global\" | false;\n /** Resume points and watch time: every `progressInterval` of playback, and on pause, seek, end, page hide and unload. */\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n onEvent?: (event: PlayerEvent) => void;\n /** Theater mode is the host's layout; the button shows with onTheaterChange. */\n theater?: boolean;\n onTheaterChange?: (theater: boolean) => void;\n /** Shows the mini-player button: the player pauses and hands over where it was. */\n onMiniPlayer?: (handoff: PlayerHandoff) => void;\n /** A control-bar slot for the host's downloads. */\n renderDownloads?: (ctx: PlayerSlotContext) => ReactNode;\n /** Host entries in the settings menu, after its own. */\n menuItems?: readonly PlayerMenuItem[];\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n label?: string;\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\nconst CONTROLS_IDLE_MS = 2500;\nconst DOUBLE_TAP_MS = 300;\nconst TAP_SEEK_CONTINUE_MS = 700;\nconst SEEK_FLASH_MS = 650;\n\nconst TEXT_ENTRY =\n 'textarea, select, [contenteditable]:not([contenteditable=\"false\"]), [role=textbox], [role=combobox], [role=searchbox], input:not([type=range]):not([type=button]):not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=reset])';\nconst MENU = \"[role=menu], [role=menuitem], [role=menuitemradio], [role=menuitemcheckbox], [role=listbox], [role=option]\";\nconst OUTSIDE_INTERACTIVE = \"a, button, input, [role=button], [role=dialog], [role=alertdialog]\";\n\n/** A track's name: its playlist name, or its language's name when the name is only the language code. */\nexport function trackLabel(tr: Pick<PlayerTrack, \"label\" | \"lang\">, languageName: (code: string) => string): string {\n const code = tr.lang && (!tr.label || tr.label.toLowerCase() === tr.lang.toLowerCase()) ? tr.lang : undefined;\n return code ? languageName(code) : tr.label;\n}\n\n/**\n * Whether a shortcut key belongs to the player: never while typing or in a\n * menu, not the arrows on a slider (it steps itself), and outside the\n * player (page-wide shortcuts) not on another control.\n */\nexport function keyAllowed(target: EventTarget | null, key: string, root: Element): boolean {\n if (!(target instanceof Element)) return true;\n if (target.closest(TEXT_ENTRY) || target.closest(MENU)) return false;\n if (target.closest(\"input[type=range], [role=slider]\") && /^(Arrow|Page|Home$|End$)/.test(key)) return false;\n return root.contains(target) || !target.closest(OUTSIDE_INTERACTIVE);\n}\n\n// Space on one of the player's buttons is play/pause, not a click on the button last used.\nconst spaceOnButton = (target: EventTarget | null, root: Element) =>\n target instanceof Element && root.contains(target) && !!target.closest(\"button\") && !target.closest(MENU);\n\n/**\n * An HLS player that never spins forever: poster and play control, its own\n * controls (seek with sprite preview, volume, subtitles, audio, quality,\n * speed, picture-in-picture, theater, fullscreen, mini player), shortcuts,\n * touch gestures, resume and progress reports, and a specific error with\n * Retry and a support code.\n */\nexport function VideoPlayer({\n base,\n poster,\n width,\n height,\n duration,\n pending,\n progress,\n failed,\n layout = \"frame\",\n maxHeight = \"80svh\",\n inlinePreview,\n previewStart,\n keyboard = \"focus\",\n onProgress,\n progressInterval = 10_000,\n onEvent,\n theater,\n onTheaterChange,\n onMiniPlayer,\n renderDownloads,\n menuItems,\n crossOrigin,\n label,\n className,\n style,\n appearance,\n startAt,\n ...options\n}: VideoPlayerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const playable = !!base && !pending && !failed;\n const src = playable ? `${base}master.m3u8` : null;\n const start = resumeAt(startAt, duration);\n const player = useHlsPlayer({ ...options, src, startAt: start });\n const { status, error, started, previewing, audio, subtitles, quality } = player;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n useInlinePreview({\n setting: inlinePreview,\n available: playable && !started && !error && options.active !== false && !options.autoPlay,\n target: root,\n playing: started && status === \"playing\",\n start: () => player.preview(previewStartAt(previewStart, duration)),\n stop: player.unload,\n });\n const media = useMediaState(video);\n const fs = useFullscreen(root, video);\n const pip = usePictureInPicture(video);\n const cues = useActiveCues(video);\n const sprite = useSprite(started && base ? `${base}sprite.vtt` : null, options.xhrSetup);\n const live = !previewing && started;\n useProgressReports(video, { enabled: live, source: live ? src : null, interval: progressInterval, onProgress });\n\n // Events.\n const emit = useRef(onEvent);\n emit.current = onEvent;\n const sending = useRef(false);\n sending.current = !previewing && status !== \"idle\";\n const send = useCallback((e: { type: string; [k: string]: unknown }, always = false) => {\n if ((always || sending.current) && emit.current) emit.current({ ...e, time: video?.currentTime ?? 0 } as PlayerEvent);\n }, [video]);\n useEffect(() => {\n if (!video) return;\n let from = 0;\n const h: Record<string, () => void> = {\n play: () => send({ type: \"play\" }),\n playing: () => send({ type: \"playing\" }),\n pause: () => !video.ended && send({ type: \"pause\" }),\n waiting: () => send({ type: \"waiting\" }),\n ended: () => send({ type: \"ended\" }),\n timeupdate: () => {\n if (!video.seeking) from = video.currentTime;\n },\n seeked: () => {\n send({ type: \"seek\", from });\n from = video.currentTime;\n },\n ratechange: () => send({ type: \"rate\", rate: video.playbackRate }),\n };\n for (const [k, f] of Object.entries(h)) video.addEventListener(k, f);\n return () => {\n for (const [k, f] of Object.entries(h)) video.removeEventListener(k, f);\n };\n }, [video, send]);\n useEffect(() => {\n if (error && !previewing) send({ type: \"error\", error }, true);\n }, [error, previewing, send]);\n const was = useRef({ fs: false, pip: false });\n useEffect(() => {\n if (was.current.fs !== fs.active) send({ type: \"fullscreen\", on: fs.active });\n if (was.current.pip !== pip.active) send({ type: \"pip\", on: pip.active });\n was.current = { fs: fs.active, pip: pip.active };\n }, [fs.active, pip.active, send]);\n\n // A new resume point for the playing source seeks to it.\n const lastStart = useRef(start);\n useEffect(() => {\n if (lastStart.current === start) return;\n lastStart.current = start;\n if (video && started) video.currentTime = start;\n }, [start, video, started]);\n\n // Controls show on pointer movement and hide after a pause in it while playing.\n const [poked, setPoked] = useState(false);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n useEffect(() => {\n if (started) poke();\n }, [started, poke]);\n const [menuOpen, setMenuOpen] = useState(false);\n const [scrubbing, setScrubbing] = useState(false);\n const [keyFocus, setKeyFocus] = useState(false);\n const controlsShown = started && !error && (poked || media.paused || media.ended || menuOpen || scrubbing || keyFocus);\n\n const [flash, setFlash] = useState<SeekFlashState | null>(null);\n const flashTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n const flashSeek = useCallback((direction: -1 | 1, seconds: number) => {\n const continuing = flashTimer.current !== undefined;\n clearTimeout(flashTimer.current);\n setFlash((prev) => ({ direction, seconds: continuing && prev?.direction === direction ? prev.seconds + seconds : seconds }));\n flashTimer.current = setTimeout(() => {\n setFlash(null);\n flashTimer.current = undefined;\n }, SEEK_FLASH_MS);\n }, []);\n useEffect(() => () => clearTimeout(flashTimer.current), []);\n\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const dur = media.duration || duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const seekBy = (seconds: number) => {\n if (!video) return;\n seek(video.currentTime + seconds);\n flashSeek(seconds < 0 ? -1 : 1, Math.abs(seconds));\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.ended) {\n if (video.ended) video.currentTime = 0;\n player.play();\n } else if (video.paused) player.play();\n else video.pause();\n };\n const setVolume = (v: number) => {\n if (!video) return;\n video.volume = Math.min(1, Math.max(0, v));\n if (video.volume > 0 && video.muted) video.muted = false;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n send({ type: \"volume\", volume: video.volume, muted: video.muted });\n };\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n send({ type: \"volume\", volume: video.volume, muted: video.muted });\n };\n const selectAudio = (i: number) => {\n audio.select(i);\n const track = audio.list[i];\n if (track) send({ type: \"audio\", track });\n };\n const lastSub = useRef(-1);\n if (subtitles.selected >= 0) lastSub.current = subtitles.selected;\n const selectSubs = (i: number) => {\n subtitles.select(i);\n send({ type: \"subtitles\", track: subtitles.list[i] ?? null });\n };\n const toggleCaptions = () => selectSubs(subtitles.selected >= 0 ? -1 : lastSub.current >= 0 ? lastSub.current : 0);\n const selectQuality = (i: number) => {\n quality.select(i);\n send({ type: \"quality\", height: quality.levels.find((l) => l.index === i)?.height ?? null });\n };\n const toggleTheater = () => {\n onTheaterChange?.(!theater);\n send({ type: \"theater\", on: !theater });\n };\n const openMini = () => {\n if (!onMiniPlayer || !base || !video) return;\n const a = audio.list[audio.selected];\n const s = subtitles.list[subtitles.selected];\n const handoff: PlayerHandoff = {\n base,\n time: video.currentTime,\n playing: !video.paused && !video.ended,\n width: width ?? (video.videoWidth || undefined),\n height: height ?? (video.videoHeight || undefined),\n duration: dur || undefined,\n poster,\n audioLanguage: a?.lang,\n subtitleLanguage: s?.lang ?? null,\n };\n video.pause();\n if (fs.active) fs.toggle();\n onMiniPlayer(handoff);\n };\n\n const run = (a: PlayerAction): boolean => {\n if (!video) return false;\n if (!started && a.type !== \"toggle\") return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n seekBy(a.seconds);\n break;\n case \"seekTo\":\n if (!dur) return false;\n seek(dur * a.fraction);\n break;\n case \"volumeBy\":\n setVolume((video.muted ? 0 : video.volume) + a.delta);\n break;\n case \"mute\":\n toggleMute();\n break;\n case \"captions\":\n if (!subtitles.list.length) return false;\n toggleCaptions();\n break;\n case \"fullscreen\":\n if (!fs.supported) return false;\n fs.toggle();\n break;\n case \"theater\":\n if (!onTheaterChange) return false;\n toggleTheater();\n break;\n case \"miniPlayer\":\n if (!onMiniPlayer) return false;\n openMini();\n break;\n case \"speed\":\n video.playbackRate = nextSpeed(video.playbackRate, a.step);\n break;\n }\n if (started) poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const active = options.active !== false;\n useEffect(() => {\n if (!keyboard || !root || !active) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action)) return;\n k.preventDefault();\n };\n const up = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.key === \" \" && spaceOnButton(k.target, root)) k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n on.addEventListener(\"keyup\", up);\n return () => {\n on.removeEventListener(\"keydown\", down);\n on.removeEventListener(\"keyup\", up);\n };\n }, [keyboard, root, active]);\n\n // Mouse: click plays and pauses, double click is fullscreen. Touch: a tap\n // shows or hides the controls; double taps on either side seek 10 s, and\n // taps that keep coming keep seeking.\n const pointerType = useRef(\"mouse\");\n const lastTap = useRef<{ at: number; side: -1 | 1 } | null>(null);\n const tapSeekUntil = useRef(0);\n const onSurfaceClick = (e: MouseEvent<HTMLDivElement>) => {\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n return;\n }\n const r = e.currentTarget.getBoundingClientRect();\n const x = r.width ? (e.clientX - r.left) / r.width : 0.5;\n const side = x < 0.4 ? -1 : x > 0.6 ? 1 : null;\n const now = Date.now();\n const last = lastTap.current;\n lastTap.current = side ? { at: now, side } : null;\n if (side && last?.side === side && (now < tapSeekUntil.current || now - last.at <= DOUBLE_TAP_MS)) {\n tapSeekUntil.current = now + TAP_SEEK_CONTINUE_MS;\n seekBy(side * JUMP_SEEK);\n return;\n }\n if (controlsShown && !media.paused) hide();\n else poke();\n };\n const onSurfaceDoubleClick = () => {\n if (Date.now() >= tapSeekUntil.current && fs.supported) fs.toggle();\n };\n\n const aspect = width && height ? width / height : (media.aspect ?? 16 / 9);\n const frame: CSSProperties = fs.active\n ? { width: \"100%\", height: \"100%\", borderRadius: 0 }\n : layout === \"frame\"\n ? { aspectRatio: String(aspect), width: `min(100%, calc(${maxHeight} * ${aspect}))`, marginInline: \"auto\" }\n : {};\n const posterOutputs = typeof poster === \"string\" ? [] : publicRenditions(poster);\n const busy = !error && !previewing && (status === \"loading\" || status === \"buffering\");\n // The preview shows once frames play; the cover stays until then.\n const shown = previewing && status === \"playing\";\n const fade = cn(\"transition-opacity duration-300 motion-reduce:transition-none\", shown && \"opacity-0\");\n const portal = fs.active && root ? root : undefined;\n const sections = settingsSections();\n\n function trackName(tr: PlayerTrack) {\n const name = trackLabel(tr, languageName) || t(\"player.track\", { number: tr.index + 1 });\n return tr.forced ? t(\"player.forced\", { label: name }) : name;\n }\n function settingsSections(): MenuSection[] {\n const out: MenuSection[] = [];\n if (audio.list.length > 1)\n out.push({ key: \"audio\", label: t(\"player.audio\"), value: String(audio.selected), options: audio.list.map((x) => ({ value: String(x.index), label: trackName(x) })), onChange: (v) => selectAudio(Number(v)) });\n if (subtitles.list.length)\n out.push({\n key: \"subtitles\",\n label: t(\"player.subtitles\"),\n value: String(subtitles.selected),\n options: [{ value: \"-1\", label: t(\"player.subtitlesOff\") }, ...subtitles.list.map((x) => ({ value: String(x.index), label: trackName(x) }))],\n onChange: (v) => selectSubs(Number(v)),\n });\n if (quality.levels.length > 1) {\n const playing = quality.levels.find((l) => l.index === quality.current);\n out.push({\n key: \"quality\",\n label: t(\"player.quality\"),\n value: String(quality.selected),\n options: [\n ...[...quality.levels].reverse().map((l) => ({ value: String(l.index), label: qualityLabel(l.height) })),\n { value: \"-1\", label: playing && quality.selected === -1 ? t(\"player.autoCurrent\", { quality: qualityLabel(playing.height).split(\" \")[0]! }) : t(\"player.auto\") },\n ],\n onChange: (v) => selectQuality(Number(v)),\n });\n }\n out.push({\n key: \"speed\",\n label: t(\"player.speed\"),\n value: String(media.rate),\n options: PLAYBACK_SPEEDS.map((r) => ({ value: String(r), label: r === 1 ? t(\"player.normal\") : `${r}×` })),\n onChange: (v) => {\n if (video) video.playbackRate = Number(v);\n },\n });\n menuItems?.forEach((m, i) => {\n if (\"options\" in m) out.push({ key: `host-${i}`, label: m.label, value: m.value, options: m.options, onChange: m.onChange });\n });\n return out;\n }\n const actions = menuItems?.filter((m): m is Extract<PlayerMenuItem, { onSelect: () => void }> => \"onSelect\" in m);\n\n return (\n <ContentKitUiRoot\n appearance={appearance}\n className={cn(\n \"@container relative w-full touch-manipulation overflow-hidden bg-black text-white outline-none\",\n layout === \"fill\" ? \"size-full\" : !fs.active && \"rounded-lg\",\n started && !controlsShown && \"cursor-none\",\n className,\n )}\n style={{ ...frame, ...style }}\n data-ckui=\"video-player\"\n data-status={failed ? \"failed\" : pending ? \"pending\" : status}\n data-previewing={previewing ? \"\" : undefined}\n data-fullscreen={fs.active ? \"\" : undefined}\n data-controls={controlsShown ? \"\" : undefined}\n ref={setRoot}\n role=\"group\"\n aria-label={label ?? t(\"player.label\")}\n tabIndex={playable ? -1 : undefined}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && started && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && !menuOpen && hide()}\n onFocus={(e) => setKeyFocus(focusVisible(e.target))}\n onBlur={() => setKeyFocus(false)}\n >\n {failed ? (\n <Panel icon>{t(\"player.failed\")}</Panel>\n ) : pending || !base ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted p-6 text-foreground\">\n <EncodeProgress progress={progress} className=\"max-w-xs\" />\n </div>\n ) : (\n <>\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {started && !error && <div data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} onDoubleClick={onSurfaceDoubleClick} />}\n {!started && !error && (\n <>\n {typeof poster === \"string\" ? (\n <img alt=\"\" src={poster} decoding=\"async\" className={cn(\"pointer-events-none absolute inset-0 size-full object-contain\", fade)} />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className={cn(\"pointer-events-none absolute inset-0 size-full object-contain\", fade)} />\n ) : (\n <SpriteFrame vtt={`${base}sprite.vtt`} xhrSetup={options.xhrSetup} className={fade} />\n )}\n <BigButton label={t(\"player.play\")} onClick={player.play} disabled={busy} className={fade}>\n {busy ? <Spinner /> : <HugeiconsIcon icon={PlayIcon} className=\"ml-1 size-7 fill-current\" strokeWidth={1.5} />}\n </BigButton>\n {duration ? (\n <span className=\"pointer-events-none absolute right-2 bottom-2 rounded bg-black/65 px-1.5 py-0.5 text-xs font-medium tabular-nums\">{formatDuration(duration)}</span>\n ) : null}\n </>\n )}\n {started && !error && media.ended && (\n <BigButton label={t(\"player.replay\")} onClick={toggle}>\n <HugeiconsIcon icon={Refresh01Icon} className=\"size-7\" strokeWidth={1.75} />\n </BigButton>\n )}\n <Captions cues={live ? cues : []} bottom={controlsShown ? \"4.75rem\" : \"1.25rem\"} />\n {started && busy && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <span className=\"flex size-14 items-center justify-center rounded-full bg-black/45\">\n <Spinner />\n </span>\n </div>\n )}\n <SeekFlash flash={flash} />\n {started && !error && (\n <div\n data-ckui=\"controls\"\n data-ckui-noswipe=\"\"\n className={cn(\n \"pointer-events-none absolute inset-x-0 bottom-0 z-10 flex flex-col bg-linear-to-t from-black/75 via-black/35 to-transparent px-2 pt-12 pb-1 transition-opacity duration-200 motion-reduce:transition-none\",\n controlsShown ? \"opacity-100 *:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <SeekBar time={media.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} sprite={sprite} />\n <div className=\"flex items-center gap-0.5\">\n <ControlButton\n label={media.ended ? t(\"player.replay\") : media.paused ? t(\"player.resume\") : t(\"player.pause\")}\n shortcut=\"k\"\n icon={media.ended ? Refresh01Icon : media.paused ? PlayIcon : PauseIcon}\n onClick={toggle}\n data-ckui=\"play\"\n />\n <VolumeControl volume={media.volume} muted={media.muted} onVolume={setVolume} onMute={toggleMute} />\n <span className=\"ml-1 shrink-0 text-xs font-medium whitespace-nowrap text-white/90 tabular-nums\" data-ckui=\"time\">\n {clock(media.time)} / {clock(dur)}\n </span>\n <div className=\"min-w-0 flex-1\" />\n {subtitles.list.length > 0 && (\n <ControlButton label={t(\"player.subtitles\")} shortcut=\"c\" icon={ClosedCaptionIcon} aria-pressed={subtitles.selected >= 0} onClick={toggleCaptions} data-ckui=\"captions-toggle\" />\n )}\n {renderDownloads?.({ container: portal, className: playerButtonClass })}\n <SettingsMenu icon={Settings01Icon} label={t(\"player.settings\")} sections={sections} actions={actions} container={portal} onOpenChange={setMenuOpen} />\n {onMiniPlayer && <ControlButton className=\"@max-sm:hidden\" label={t(\"player.miniPlayer\")} shortcut=\"i\" icon={PictureInPictureOnIcon} onClick={openMini} data-ckui=\"mini-player-open\" />}\n {pip.supported && (\n <ControlButton\n className=\"@max-md:hidden\"\n label={pip.active ? t(\"player.exitPip\") : t(\"player.pip\")}\n icon={pip.active ? PictureInPictureExitIcon : PictureInPicture01Icon}\n onClick={pip.toggle}\n data-ckui=\"pip\"\n />\n )}\n {onTheaterChange && (\n <ControlButton\n className=\"max-lg:hidden\"\n label={t(\"player.theater\")}\n shortcut=\"t\"\n icon={RectangularIcon}\n aria-pressed={!!theater}\n onClick={toggleTheater}\n data-ckui=\"theater\"\n />\n )}\n {fs.supported && (\n <ControlButton\n label={fs.active ? t(\"player.exitFullscreen\") : t(\"player.fullscreen\")}\n shortcut=\"f\"\n icon={fs.active ? ArrowShrink02Icon : FullScreenIcon}\n onClick={fs.toggle}\n data-ckui=\"fullscreen\"\n />\n )}\n </div>\n </div>\n )}\n <span className=\"sr-only\" aria-live=\"polite\">\n {busy ? t(\"player.loading\") : \"\"}\n </span>\n {error && (\n <Panel icon code={error.code} onRetry={player.retry}>\n {t(`player.errors.${error.kind}`)}\n </Panel>\n )}\n </>\n )}\n </ContentKitUiRoot>\n );\n}\n\nfunction focusVisible(el: EventTarget) {\n try {\n return el instanceof Element && el.matches(\":focus-visible\") && !!el.closest(\"[data-ckui=controls]\");\n } catch {\n return false;\n }\n}\n\nfunction BigButton({ label, onClick, disabled, className, children }: { label: string; onClick: () => void; disabled?: boolean; className?: string; children: ReactNode }) {\n return (\n <button\n type=\"button\"\n className=\"absolute inset-0 z-[7] flex items-center justify-center outline-none focus-visible:[&>span]:ring-4 focus-visible:[&>span]:ring-white/60\"\n onClick={onClick}\n aria-label={label}\n disabled={disabled}\n >\n <span className={cn(\"flex size-16 items-center justify-center rounded-full bg-black/55 shadow-lg backdrop-blur-sm transition motion-safe:hover:scale-105\", className)}>{children}</span>\n </button>\n );\n}\n\n/** A preview's start: the given second, else 10% in, kept a second before the end. */\nexport function previewStartAt(start: number | undefined, duration: number | undefined): number {\n const d = duration ?? 0;\n const t = start ?? d * 0.1;\n return Math.max(0, d > 1 ? Math.min(t, d - 1) : 0);\n}\n\n/** \"2160p 4K\", \"1080p HD\", \"720p\": a rung by its short side. */\nexport function qualityLabel(height: number): string {\n return `${height}p${height >= 2160 ? \" 4K\" : height === 1080 ? \" HD\" : \"\"}`;\n}\n\nfunction Panel({ children, icon, code, onRetry }: { children: string; icon?: boolean; code?: string; onRetry?: () => void }) {\n const { t } = useMessages();\n return (\n <div role=\"alert\" data-ckui=\"player-error\" data-ckui-noswipe=\"\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-3 bg-black/80 p-6 text-center\">\n {icon && <HugeiconsIcon icon={AlertCircleIcon} className=\"size-7 text-white/80\" strokeWidth={1.75} />}\n <p className=\"max-w-sm text-sm font-medium text-balance\">{children}</p>\n {onRetry && (\n <Button size=\"sm\" variant=\"secondary\" onClick={onRetry}>\n <HugeiconsIcon icon={Refresh01Icon} data-icon=\"inline-start\" />\n {t(\"player.retry\")}\n </Button>\n )}\n {code && <p className=\"font-mono text-[11px] text-white/55\">{t(\"player.code\", { code })}</p>}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"video-player.js","names":[],"sources":["../../src/components/video-player.tsx"],"sourcesContent":["import {\n AlertCircleIcon,\n ArrowShrink02Icon,\n ClosedCaptionIcon,\n FullScreenIcon,\n PauseIcon,\n PictureInPicture01Icon,\n PictureInPictureExitIcon,\n PictureInPictureOnIcon,\n PlayIcon,\n RectangularIcon,\n Refresh01Icon,\n Settings01Icon,\n} from \"@hugeicons/core-free-icons\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { cn } from \"cn\";\nimport { useCallback, useEffect, useRef, useState, type CSSProperties, type MouseEvent, type ReactNode } from \"react\";\nimport type { ContentKitUiAppearance } from \"../appearance.js\";\nimport { formatDuration } from \"../client/gallery.js\";\nimport type { PlaybackError } from \"../client/playback.js\";\nimport {\n JUMP_SEEK,\n PLAYBACK_SPEEDS,\n PLAYER_VOLUME_KEY,\n nextSpeed,\n playerKeyAction,\n resumeAt,\n writeVolume,\n type PlayerAction,\n type PlayerTrack,\n} from \"../client/player.js\";\nimport { useHlsPlayer, type HlsPlayerOptions } from \"../react/gallery.js\";\nimport { useInlinePreview } from \"../react/inline-preview.js\";\nimport { useActiveCues, useFullscreen, useMediaState, usePictureInPicture, useProgressReports, useSprite, type PlaybackProgress } from \"../react/player.js\";\nimport { useLanguageName, useMessages } from \"../i18n/context.js\";\nimport { ContentKitUiRoot } from \"../scope.js\";\nimport { RenditionImg } from \"./rendition-img.js\";\nimport { publicRenditions, type PublicPreset } from \"../client/public.js\";\nimport type { EncodeProgress as Progress } from \"../client/generated/wire.js\";\nimport { EncodeProgress } from \"./encode-progress.js\";\nimport { Button } from \"#ckui/ui/button\";\nimport { Captions, ControlButton, SeekBar, SeekFlash, SettingsMenu, Spinner, VolumeControl, clock, playerButtonClass, type MenuSection, type SeekFlashState } from \"./player-controls.js\";\n\ntype Loader = HlsPlayerOptions[\"loader\"];\n\n/** The first frame of a video's seek sprite (sprite.vtt), fitted like object-fit. */\nexport function SpriteFrame({ vtt, loader, fit = \"contain\", className }: { vtt: string; loader?: Loader; fit?: \"contain\" | \"cover\"; className?: string }) {\n const sprite = useSprite(vtt, loader);\n const cue = sprite?.cues[0];\n if (!sprite || !cue) return null;\n return (\n <svg\n aria-hidden\n className={cn(\"pointer-events-none absolute inset-0 size-full\", className)}\n viewBox={`${cue.x} ${cue.y} ${cue.w} ${cue.h}`}\n preserveAspectRatio={fit === \"cover\" ? \"xMidYMid slice\" : \"xMidYMid meet\"}\n data-ckui=\"sprite-frame\"\n >\n <image href={cue.url} width={sprite.width} height={sprite.height} />\n </svg>\n );\n}\n\ntype At<T> = T extends unknown ? T & { time: number } : never;\n\n/** Playback as it happens, for analytics; never during an inline preview. */\nexport type PlayerEvent = At<\n | { type: \"play\" | \"playing\" | \"pause\" | \"waiting\" | \"ended\" }\n | { type: \"seek\"; from: number }\n | { type: \"rate\"; rate: number }\n | { type: \"volume\"; volume: number; muted: boolean }\n /** The viewer's quality choice; height null is Auto. */\n | { type: \"quality\"; height: number | null }\n | { type: \"audio\"; track: PlayerTrack }\n | { type: \"subtitles\"; track: PlayerTrack | null }\n | { type: \"fullscreen\" | \"pip\" | \"theater\"; on: boolean }\n | { type: \"error\"; error: PlaybackError }\n>;\n\n/** Everything a VideoMiniPlayer needs to carry on where the player stopped. */\nexport interface PlayerHandoff {\n base: string;\n time: number;\n playing: boolean;\n width?: number;\n height?: number;\n duration?: number;\n poster?: string | PublicPreset | null;\n audioLanguage?: string;\n subtitleLanguage?: string | null;\n}\n\n/** A host entry in the settings menu: a choice (e.g. version) or an action (e.g. report a problem). */\nexport type PlayerMenuItem =\n | { label: string; value: string; options: readonly { value: string; label: string }[]; onChange: (value: string) => void }\n | { label: string; onSelect: () => void };\n\nexport interface PlayerSlotContext {\n /** Where popups must portal to stay visible: the player while fullscreen, else undefined (the body). */\n container?: HTMLElement;\n /** The control buttons' class. */\n className: string;\n}\n\nexport interface VideoPlayerProps extends Omit<HlsPlayerOptions, \"src\"> {\n /** The ladder's HLS folder (client.media.hlsBase: {media}/{kind}/{id}/hls/{dir}): master.m3u8 and sprite.vtt resolve under it. */\n base?: string | null;\n /** Shown until playback starts: the poster's public preset or a URL; default the first sprite frame. */\n poster?: string | PublicPreset | null;\n /** The video's size (read API w/h): the box is reserved before anything loads; default the decoded frame's. */\n width?: number;\n height?: number;\n duration?: number;\n /** A pending encode (an editor read's `pending` and `progress`): shows its progress instead of a player. */\n pending?: boolean;\n progress?: Progress | null;\n /** Why the video cannot be encoded (an editor read's `failed.message`). */\n failed?: string;\n /**\n * `frame` is as wide as its column allows at the video's aspect, never\n * taller than `maxHeight`; `fill` fills a sized parent (a carousel slide).\n */\n layout?: \"frame\" | \"fill\";\n maxHeight?: string;\n /** Preview muted inline before play (hover, or in view on touch); default the provider's. */\n inlinePreview?: boolean;\n /** Where the preview starts, seconds; default 10% in. */\n previewStart?: number;\n /** Shortcuts while focus is in the player (default), page-wide (a watch page: one such player per page), or none. */\n keyboard?: \"focus\" | \"global\" | false;\n /** Resume points and watch time: every `progressInterval` of playback, and on pause, seek, end, page hide and unload. */\n onProgress?: (progress: PlaybackProgress) => void;\n /** Milliseconds. Default 10000. */\n progressInterval?: number;\n onEvent?: (event: PlayerEvent) => void;\n /** Theater mode is the host's layout; the button shows with onTheaterChange. */\n theater?: boolean;\n onTheaterChange?: (theater: boolean) => void;\n /** Shows the mini-player button: the player pauses and hands over where it was. */\n onMiniPlayer?: (handoff: PlayerHandoff) => void;\n /** A control-bar slot for the host's downloads. */\n renderDownloads?: (ctx: PlayerSlotContext) => ReactNode;\n /** Host entries in the settings menu, after its own. */\n menuItems?: readonly PlayerMenuItem[];\n crossOrigin?: \"anonymous\" | \"use-credentials\";\n label?: string;\n className?: string;\n style?: CSSProperties;\n appearance?: ContentKitUiAppearance;\n}\n\nconst CONTROLS_IDLE_MS = 2500;\nconst DOUBLE_TAP_MS = 300;\nconst TAP_SEEK_CONTINUE_MS = 700;\nconst SEEK_FLASH_MS = 650;\n\nconst TEXT_ENTRY =\n 'textarea, select, [contenteditable]:not([contenteditable=\"false\"]), [role=textbox], [role=combobox], [role=searchbox], input:not([type=range]):not([type=button]):not([type=checkbox]):not([type=radio]):not([type=submit]):not([type=reset])';\nconst MENU = \"[role=menu], [role=menuitem], [role=menuitemradio], [role=menuitemcheckbox], [role=listbox], [role=option]\";\nconst OUTSIDE_INTERACTIVE = \"a, button, input, [role=button], [role=dialog], [role=alertdialog]\";\n\n/** A track's name: its playlist name, or its language's name when the name is only the language code. */\nexport function trackLabel(tr: Pick<PlayerTrack, \"label\" | \"lang\">, languageName: (code: string) => string): string {\n const code = tr.lang && (!tr.label || tr.label.toLowerCase() === tr.lang.toLowerCase()) ? tr.lang : undefined;\n return code ? languageName(code) : tr.label;\n}\n\n/**\n * Whether a shortcut key belongs to the player: never while typing or in a\n * menu, not the arrows on a slider (it steps itself), and outside the\n * player (page-wide shortcuts) not on another control.\n */\nexport function keyAllowed(target: EventTarget | null, key: string, root: Element): boolean {\n if (!(target instanceof Element)) return true;\n if (target.closest(TEXT_ENTRY) || target.closest(MENU)) return false;\n if (target.closest(\"input[type=range], [role=slider]\") && /^(Arrow|Page|Home$|End$)/.test(key)) return false;\n return root.contains(target) || !target.closest(OUTSIDE_INTERACTIVE);\n}\n\n// Space on one of the player's buttons is play/pause, not a click on the button last used.\nconst spaceOnButton = (target: EventTarget | null, root: Element) =>\n target instanceof Element && root.contains(target) && !!target.closest(\"button\") && !target.closest(MENU);\n\n/**\n * An HLS player that never spins forever: poster and play control, its own\n * controls (seek with sprite preview, volume, subtitles, audio, quality,\n * speed, picture-in-picture, theater, fullscreen, mini player), shortcuts,\n * touch gestures, resume and progress reports, and a specific error with\n * Retry and a support code.\n */\nexport function VideoPlayer({\n base,\n poster,\n width,\n height,\n duration,\n pending,\n progress,\n failed,\n layout = \"frame\",\n maxHeight = \"80svh\",\n inlinePreview,\n previewStart,\n keyboard = \"focus\",\n onProgress,\n progressInterval = 10_000,\n onEvent,\n theater,\n onTheaterChange,\n onMiniPlayer,\n renderDownloads,\n menuItems,\n crossOrigin,\n label,\n className,\n style,\n appearance,\n startAt,\n ...options\n}: VideoPlayerProps) {\n const { t } = useMessages();\n const languageName = useLanguageName();\n const playable = !!base && !pending && !failed;\n const src = playable ? `${base}master.m3u8` : null;\n const start = resumeAt(startAt, duration);\n const player = useHlsPlayer({ ...options, src, startAt: start });\n const { status, error, started, previewing, audio, subtitles, quality } = player;\n const [root, setRoot] = useState<HTMLDivElement | null>(null);\n const [video, setVideo] = useState<HTMLVideoElement | null>(null);\n const attach = player.ref;\n const videoRef = useCallback(\n (el: HTMLVideoElement | null) => {\n attach(el);\n setVideo(el);\n },\n [attach],\n );\n useInlinePreview({\n setting: inlinePreview,\n available: playable && !started && !error && options.active !== false && !options.autoPlay,\n target: root,\n playing: started && status === \"playing\",\n start: () => player.preview(previewStartAt(previewStart, duration)),\n stop: player.unload,\n });\n const media = useMediaState(video);\n const fs = useFullscreen(root, video);\n const pip = usePictureInPicture(video);\n const cues = useActiveCues(video);\n const sprite = useSprite(started && base ? `${base}sprite.vtt` : null, options.loader);\n const live = !previewing && started;\n useProgressReports(video, { enabled: live, source: live ? src : null, interval: progressInterval, onProgress });\n\n // Events.\n const emit = useRef(onEvent);\n emit.current = onEvent;\n const sending = useRef(false);\n sending.current = !previewing && status !== \"idle\";\n const send = useCallback((e: { type: string; [k: string]: unknown }, always = false) => {\n if ((always || sending.current) && emit.current) emit.current({ ...e, time: video?.currentTime ?? 0 } as PlayerEvent);\n }, [video]);\n useEffect(() => {\n if (!video) return;\n let from = 0;\n const h: Record<string, () => void> = {\n play: () => send({ type: \"play\" }),\n playing: () => send({ type: \"playing\" }),\n pause: () => !video.ended && send({ type: \"pause\" }),\n waiting: () => send({ type: \"waiting\" }),\n ended: () => send({ type: \"ended\" }),\n timeupdate: () => {\n if (!video.seeking) from = video.currentTime;\n },\n seeked: () => {\n send({ type: \"seek\", from });\n from = video.currentTime;\n },\n ratechange: () => send({ type: \"rate\", rate: video.playbackRate }),\n };\n for (const [k, f] of Object.entries(h)) video.addEventListener(k, f);\n return () => {\n for (const [k, f] of Object.entries(h)) video.removeEventListener(k, f);\n };\n }, [video, send]);\n useEffect(() => {\n if (error && !previewing) send({ type: \"error\", error }, true);\n }, [error, previewing, send]);\n const was = useRef({ fs: false, pip: false });\n useEffect(() => {\n if (was.current.fs !== fs.active) send({ type: \"fullscreen\", on: fs.active });\n if (was.current.pip !== pip.active) send({ type: \"pip\", on: pip.active });\n was.current = { fs: fs.active, pip: pip.active };\n }, [fs.active, pip.active, send]);\n\n // A new resume point for the playing source seeks to it.\n const lastStart = useRef(start);\n useEffect(() => {\n if (lastStart.current === start) return;\n lastStart.current = start;\n if (video && started) video.currentTime = start;\n }, [start, video, started]);\n\n // Controls show on pointer movement and hide after a pause in it while playing.\n const [poked, setPoked] = useState(false);\n const idle = useRef<ReturnType<typeof setTimeout>>(undefined);\n const poke = useCallback(() => {\n setPoked(true);\n clearTimeout(idle.current);\n idle.current = setTimeout(() => setPoked(false), CONTROLS_IDLE_MS);\n }, []);\n const hide = useCallback(() => {\n clearTimeout(idle.current);\n setPoked(false);\n }, []);\n useEffect(() => () => clearTimeout(idle.current), []);\n useEffect(() => {\n if (started) poke();\n }, [started, poke]);\n const [menuOpen, setMenuOpen] = useState(false);\n const [scrubbing, setScrubbing] = useState(false);\n const [keyFocus, setKeyFocus] = useState(false);\n const controlsShown = started && !error && (poked || media.paused || media.ended || menuOpen || scrubbing || keyFocus);\n\n const [flash, setFlash] = useState<SeekFlashState | null>(null);\n const flashTimer = useRef<ReturnType<typeof setTimeout>>(undefined);\n const flashSeek = useCallback((direction: -1 | 1, seconds: number) => {\n const continuing = flashTimer.current !== undefined;\n clearTimeout(flashTimer.current);\n setFlash((prev) => ({ direction, seconds: continuing && prev?.direction === direction ? prev.seconds + seconds : seconds }));\n flashTimer.current = setTimeout(() => {\n setFlash(null);\n flashTimer.current = undefined;\n }, SEEK_FLASH_MS);\n }, []);\n useEffect(() => () => clearTimeout(flashTimer.current), []);\n\n const volumeKey = options.volumeKey === undefined ? PLAYER_VOLUME_KEY : options.volumeKey;\n const dur = media.duration || duration || 0;\n const seek = (to: number) => {\n if (video) video.currentTime = Math.min(dur || to, Math.max(0, to));\n };\n const seekBy = (seconds: number) => {\n if (!video) return;\n seek(video.currentTime + seconds);\n flashSeek(seconds < 0 ? -1 : 1, Math.abs(seconds));\n };\n const toggle = () => {\n if (!video) return;\n if (!started || video.ended) {\n if (video.ended) video.currentTime = 0;\n player.play();\n } else if (video.paused) player.play();\n else video.pause();\n };\n const setVolume = (v: number) => {\n if (!video) return;\n video.volume = Math.min(1, Math.max(0, v));\n if (video.volume > 0 && video.muted) video.muted = false;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n send({ type: \"volume\", volume: video.volume, muted: video.muted });\n };\n const toggleMute = () => {\n if (!video) return;\n video.muted = !video.muted;\n if (!video.muted && video.volume === 0) video.volume = 1;\n writeVolume(volumeKey, { volume: video.volume, muted: video.muted });\n send({ type: \"volume\", volume: video.volume, muted: video.muted });\n };\n const selectAudio = (i: number) => {\n audio.select(i);\n const track = audio.list[i];\n if (track) send({ type: \"audio\", track });\n };\n const lastSub = useRef(-1);\n if (subtitles.selected >= 0) lastSub.current = subtitles.selected;\n const selectSubs = (i: number) => {\n subtitles.select(i);\n send({ type: \"subtitles\", track: subtitles.list[i] ?? null });\n };\n const toggleCaptions = () => selectSubs(subtitles.selected >= 0 ? -1 : lastSub.current >= 0 ? lastSub.current : 0);\n const selectQuality = (i: number) => {\n quality.select(i);\n send({ type: \"quality\", height: quality.levels.find((l) => l.index === i)?.height ?? null });\n };\n const toggleTheater = () => {\n onTheaterChange?.(!theater);\n send({ type: \"theater\", on: !theater });\n };\n const openMini = () => {\n if (!onMiniPlayer || !base || !video) return;\n const a = audio.list[audio.selected];\n const s = subtitles.list[subtitles.selected];\n const handoff: PlayerHandoff = {\n base,\n time: video.currentTime,\n playing: !video.paused && !video.ended,\n width: width ?? (video.videoWidth || undefined),\n height: height ?? (video.videoHeight || undefined),\n duration: dur || undefined,\n poster,\n audioLanguage: a?.lang,\n subtitleLanguage: s?.lang ?? null,\n };\n video.pause();\n if (fs.active) fs.toggle();\n onMiniPlayer(handoff);\n };\n\n const run = (a: PlayerAction): boolean => {\n if (!video) return false;\n if (!started && a.type !== \"toggle\") return false;\n switch (a.type) {\n case \"toggle\":\n toggle();\n break;\n case \"seekBy\":\n seekBy(a.seconds);\n break;\n case \"seekTo\":\n if (!dur) return false;\n seek(dur * a.fraction);\n break;\n case \"volumeBy\":\n setVolume((video.muted ? 0 : video.volume) + a.delta);\n break;\n case \"mute\":\n toggleMute();\n break;\n case \"captions\":\n if (!subtitles.list.length) return false;\n toggleCaptions();\n break;\n case \"fullscreen\":\n if (!fs.supported) return false;\n fs.toggle();\n break;\n case \"theater\":\n if (!onTheaterChange) return false;\n toggleTheater();\n break;\n case \"miniPlayer\":\n if (!onMiniPlayer) return false;\n openMini();\n break;\n case \"speed\":\n video.playbackRate = nextSpeed(video.playbackRate, a.step);\n break;\n }\n if (started) poke();\n return true;\n };\n const runRef = useRef(run);\n runRef.current = run;\n const active = options.active !== false;\n useEffect(() => {\n if (!keyboard || !root || !active) return;\n const on: EventTarget = keyboard === \"global\" ? document : root;\n const down = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.defaultPrevented) return;\n const action = playerKeyAction(k);\n if (!action || !keyAllowed(k.target, k.key, root) || !runRef.current(action)) return;\n k.preventDefault();\n };\n const up = (e: Event) => {\n const k = e as KeyboardEvent;\n if (k.key === \" \" && spaceOnButton(k.target, root)) k.preventDefault();\n };\n on.addEventListener(\"keydown\", down);\n on.addEventListener(\"keyup\", up);\n return () => {\n on.removeEventListener(\"keydown\", down);\n on.removeEventListener(\"keyup\", up);\n };\n }, [keyboard, root, active]);\n\n // Mouse: click plays and pauses, double click is fullscreen. Touch: a tap\n // shows or hides the controls; double taps on either side seek 10 s, and\n // taps that keep coming keep seeking.\n const pointerType = useRef(\"mouse\");\n const lastTap = useRef<{ at: number; side: -1 | 1 } | null>(null);\n const tapSeekUntil = useRef(0);\n const onSurfaceClick = (e: MouseEvent<HTMLDivElement>) => {\n if (pointerType.current === \"mouse\") {\n toggle();\n poke();\n return;\n }\n const r = e.currentTarget.getBoundingClientRect();\n const x = r.width ? (e.clientX - r.left) / r.width : 0.5;\n const side = x < 0.4 ? -1 : x > 0.6 ? 1 : null;\n const now = Date.now();\n const last = lastTap.current;\n lastTap.current = side ? { at: now, side } : null;\n if (side && last?.side === side && (now < tapSeekUntil.current || now - last.at <= DOUBLE_TAP_MS)) {\n tapSeekUntil.current = now + TAP_SEEK_CONTINUE_MS;\n seekBy(side * JUMP_SEEK);\n return;\n }\n if (controlsShown && !media.paused) hide();\n else poke();\n };\n const onSurfaceDoubleClick = () => {\n if (Date.now() >= tapSeekUntil.current && fs.supported) fs.toggle();\n };\n\n const aspect = width && height ? width / height : (media.aspect ?? 16 / 9);\n const frame: CSSProperties = fs.active\n ? { width: \"100%\", height: \"100%\", borderRadius: 0 }\n : layout === \"frame\"\n ? { aspectRatio: String(aspect), width: `min(100%, calc(${maxHeight} * ${aspect}))`, marginInline: \"auto\" }\n : {};\n const posterOutputs = typeof poster === \"string\" ? [] : publicRenditions(poster);\n const busy = !error && !previewing && (status === \"loading\" || status === \"buffering\");\n // The preview shows once frames play; the cover stays until then.\n const shown = previewing && status === \"playing\";\n const fade = cn(\"transition-opacity duration-300 motion-reduce:transition-none\", shown && \"opacity-0\");\n const portal = fs.active && root ? root : undefined;\n const sections = settingsSections();\n\n function trackName(tr: PlayerTrack) {\n const name = trackLabel(tr, languageName) || t(\"player.track\", { number: tr.index + 1 });\n return tr.forced ? t(\"player.forced\", { label: name }) : name;\n }\n function settingsSections(): MenuSection[] {\n const out: MenuSection[] = [];\n if (audio.list.length > 1)\n out.push({ key: \"audio\", label: t(\"player.audio\"), value: String(audio.selected), options: audio.list.map((x) => ({ value: String(x.index), label: trackName(x) })), onChange: (v) => selectAudio(Number(v)) });\n if (subtitles.list.length)\n out.push({\n key: \"subtitles\",\n label: t(\"player.subtitles\"),\n value: String(subtitles.selected),\n options: [{ value: \"-1\", label: t(\"player.subtitlesOff\") }, ...subtitles.list.map((x) => ({ value: String(x.index), label: trackName(x) }))],\n onChange: (v) => selectSubs(Number(v)),\n });\n if (quality.levels.length > 1) {\n const playing = quality.levels.find((l) => l.index === quality.current);\n out.push({\n key: \"quality\",\n label: t(\"player.quality\"),\n value: String(quality.selected),\n options: [\n ...[...quality.levels].reverse().map((l) => ({ value: String(l.index), label: qualityLabel(l.height) })),\n { value: \"-1\", label: playing && quality.selected === -1 ? t(\"player.autoCurrent\", { quality: qualityLabel(playing.height).split(\" \")[0]! }) : t(\"player.auto\") },\n ],\n onChange: (v) => selectQuality(Number(v)),\n });\n }\n out.push({\n key: \"speed\",\n label: t(\"player.speed\"),\n value: String(media.rate),\n options: PLAYBACK_SPEEDS.map((r) => ({ value: String(r), label: r === 1 ? t(\"player.normal\") : `${r}×` })),\n onChange: (v) => {\n if (video) video.playbackRate = Number(v);\n },\n });\n menuItems?.forEach((m, i) => {\n if (\"options\" in m) out.push({ key: `host-${i}`, label: m.label, value: m.value, options: m.options, onChange: m.onChange });\n });\n return out;\n }\n const actions = menuItems?.filter((m): m is Extract<PlayerMenuItem, { onSelect: () => void }> => \"onSelect\" in m);\n\n return (\n <ContentKitUiRoot\n appearance={appearance}\n className={cn(\n \"@container relative w-full touch-manipulation overflow-hidden bg-black text-white outline-none\",\n layout === \"fill\" ? \"size-full\" : !fs.active && \"rounded-lg\",\n started && !controlsShown && \"cursor-none\",\n className,\n )}\n style={{ ...frame, ...style }}\n data-ckui=\"video-player\"\n data-status={failed ? \"failed\" : pending ? \"pending\" : status}\n data-previewing={previewing ? \"\" : undefined}\n data-fullscreen={fs.active ? \"\" : undefined}\n data-controls={controlsShown ? \"\" : undefined}\n ref={setRoot}\n role=\"group\"\n aria-label={label ?? t(\"player.label\")}\n tabIndex={playable ? -1 : undefined}\n onPointerDown={(e) => (pointerType.current = e.pointerType || \"mouse\")}\n onPointerMove={(e) => e.pointerType === \"mouse\" && started && poke()}\n onPointerLeave={(e) => e.pointerType === \"mouse\" && !scrubbing && !menuOpen && hide()}\n onFocus={(e) => setKeyFocus(focusVisible(e.target))}\n onBlur={() => setKeyFocus(false)}\n >\n {failed ? (\n <Panel icon>{t(\"player.failed\")}</Panel>\n ) : pending || !base ? (\n <div className=\"absolute inset-0 flex items-center justify-center bg-muted p-6 text-foreground\">\n <EncodeProgress progress={progress} className=\"max-w-xs\" />\n </div>\n ) : (\n <>\n <video ref={videoRef} className=\"absolute inset-0 size-full object-contain\" playsInline preload=\"none\" crossOrigin={crossOrigin} aria-label={label} />\n {started && !error && <div data-ckui=\"surface\" className=\"absolute inset-0\" onClick={onSurfaceClick} onDoubleClick={onSurfaceDoubleClick} />}\n {!started && !error && (\n <>\n {typeof poster === \"string\" ? (\n <img alt=\"\" src={poster} decoding=\"async\" className={cn(\"pointer-events-none absolute inset-0 size-full object-contain\", fade)} />\n ) : posterOutputs.length ? (\n <RenditionImg outputs={posterOutputs} className={cn(\"pointer-events-none absolute inset-0 size-full object-contain\", fade)} />\n ) : (\n <SpriteFrame vtt={`${base}sprite.vtt`} loader={options.loader} className={fade} />\n )}\n <BigButton label={t(\"player.play\")} onClick={player.play} disabled={busy} className={fade}>\n {busy ? <Spinner /> : <HugeiconsIcon icon={PlayIcon} className=\"ml-1 size-7 fill-current\" strokeWidth={1.5} />}\n </BigButton>\n {duration ? (\n <span className=\"pointer-events-none absolute right-2 bottom-2 rounded bg-black/65 px-1.5 py-0.5 text-xs font-medium tabular-nums\">{formatDuration(duration)}</span>\n ) : null}\n </>\n )}\n {started && !error && media.ended && (\n <BigButton label={t(\"player.replay\")} onClick={toggle}>\n <HugeiconsIcon icon={Refresh01Icon} className=\"size-7\" strokeWidth={1.75} />\n </BigButton>\n )}\n <Captions cues={live ? cues : []} bottom={controlsShown ? \"4.75rem\" : \"1.25rem\"} />\n {started && busy && (\n <div className=\"pointer-events-none absolute inset-0 flex items-center justify-center\">\n <span className=\"flex size-14 items-center justify-center rounded-full bg-black/45\">\n <Spinner />\n </span>\n </div>\n )}\n <SeekFlash flash={flash} />\n {started && !error && (\n <div\n data-ckui=\"controls\"\n data-ckui-noswipe=\"\"\n className={cn(\n \"pointer-events-none absolute inset-x-0 bottom-0 z-10 flex flex-col bg-linear-to-t from-black/75 via-black/35 to-transparent px-2 pt-12 pb-1 transition-opacity duration-200 motion-reduce:transition-none\",\n controlsShown ? \"opacity-100 *:pointer-events-auto\" : \"opacity-0\",\n )}\n >\n <SeekBar time={media.time} duration={dur} buffered={media.buffered} onSeek={seek} onScrub={(v) => setScrubbing(v !== null)} sprite={sprite} />\n <div className=\"flex items-center gap-0.5\">\n <ControlButton\n label={media.ended ? t(\"player.replay\") : media.paused ? t(\"player.resume\") : t(\"player.pause\")}\n shortcut=\"k\"\n icon={media.ended ? Refresh01Icon : media.paused ? PlayIcon : PauseIcon}\n onClick={toggle}\n data-ckui=\"play\"\n />\n <VolumeControl volume={media.volume} muted={media.muted} onVolume={setVolume} onMute={toggleMute} />\n <span className=\"ml-1 shrink-0 text-xs font-medium whitespace-nowrap text-white/90 tabular-nums\" data-ckui=\"time\">\n {clock(media.time)} / {clock(dur)}\n </span>\n <div className=\"min-w-0 flex-1\" />\n {subtitles.list.length > 0 && (\n <ControlButton label={t(\"player.subtitles\")} shortcut=\"c\" icon={ClosedCaptionIcon} aria-pressed={subtitles.selected >= 0} onClick={toggleCaptions} data-ckui=\"captions-toggle\" />\n )}\n {renderDownloads?.({ container: portal, className: playerButtonClass })}\n <SettingsMenu icon={Settings01Icon} label={t(\"player.settings\")} sections={sections} actions={actions} container={portal} onOpenChange={setMenuOpen} />\n {onMiniPlayer && <ControlButton className=\"@max-sm:hidden\" label={t(\"player.miniPlayer\")} shortcut=\"i\" icon={PictureInPictureOnIcon} onClick={openMini} data-ckui=\"mini-player-open\" />}\n {pip.supported && (\n <ControlButton\n className=\"@max-md:hidden\"\n label={pip.active ? t(\"player.exitPip\") : t(\"player.pip\")}\n icon={pip.active ? PictureInPictureExitIcon : PictureInPicture01Icon}\n onClick={pip.toggle}\n data-ckui=\"pip\"\n />\n )}\n {onTheaterChange && (\n <ControlButton\n className=\"max-lg:hidden\"\n label={t(\"player.theater\")}\n shortcut=\"t\"\n icon={RectangularIcon}\n aria-pressed={!!theater}\n onClick={toggleTheater}\n data-ckui=\"theater\"\n />\n )}\n {fs.supported && (\n <ControlButton\n label={fs.active ? t(\"player.exitFullscreen\") : t(\"player.fullscreen\")}\n shortcut=\"f\"\n icon={fs.active ? ArrowShrink02Icon : FullScreenIcon}\n onClick={fs.toggle}\n data-ckui=\"fullscreen\"\n />\n )}\n </div>\n </div>\n )}\n <span className=\"sr-only\" aria-live=\"polite\">\n {busy ? t(\"player.loading\") : \"\"}\n </span>\n {error && (\n <Panel icon code={error.code} onRetry={player.retry}>\n {t(`player.errors.${error.kind}`)}\n </Panel>\n )}\n </>\n )}\n </ContentKitUiRoot>\n );\n}\n\nfunction focusVisible(el: EventTarget) {\n try {\n return el instanceof Element && el.matches(\":focus-visible\") && !!el.closest(\"[data-ckui=controls]\");\n } catch {\n return false;\n }\n}\n\nfunction BigButton({ label, onClick, disabled, className, children }: { label: string; onClick: () => void; disabled?: boolean; className?: string; children: ReactNode }) {\n return (\n <button\n type=\"button\"\n className=\"absolute inset-0 z-[7] flex items-center justify-center outline-none focus-visible:[&>span]:ring-4 focus-visible:[&>span]:ring-white/60\"\n onClick={onClick}\n aria-label={label}\n disabled={disabled}\n >\n <span className={cn(\"flex size-16 items-center justify-center rounded-full bg-black/55 shadow-lg backdrop-blur-sm transition motion-safe:hover:scale-105\", className)}>{children}</span>\n </button>\n );\n}\n\n/** A preview's start: the given second, else 10% in, kept a second before the end. */\nexport function previewStartAt(start: number | undefined, duration: number | undefined): number {\n const d = duration ?? 0;\n const t = start ?? d * 0.1;\n return Math.max(0, d > 1 ? Math.min(t, d - 1) : 0);\n}\n\n/** \"2160p 4K\", \"1080p HD\", \"720p\": a rung by its short side. */\nexport function qualityLabel(height: number): string {\n return `${height}p${height >= 2160 ? \" 4K\" : height === 1080 ? \" HD\" : \"\"}`;\n}\n\nfunction Panel({ children, icon, code, onRetry }: { children: string; icon?: boolean; code?: string; onRetry?: () => void }) {\n const { t } = useMessages();\n return (\n <div role=\"alert\" data-ckui=\"player-error\" data-ckui-noswipe=\"\" className=\"absolute inset-0 z-20 flex flex-col items-center justify-center gap-3 bg-black/80 p-6 text-center\">\n {icon && <HugeiconsIcon icon={AlertCircleIcon} className=\"size-7 text-white/80\" strokeWidth={1.75} />}\n <p className=\"max-w-sm text-sm font-medium text-balance\">{children}</p>\n {onRetry && (\n <Button size=\"sm\" variant=\"secondary\" onClick={onRetry}>\n <HugeiconsIcon icon={Refresh01Icon} data-icon=\"inline-start\" />\n {t(\"player.retry\")}\n </Button>\n )}\n {code && <p className=\"font-mono text-[11px] text-white/55\">{t(\"player.code\", { code })}</p>}\n </div>\n );\n}\n"],"mappings":"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"}
|
package/dist/react/gallery.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { KeyboardEvent, MouseEvent, PointerEvent } from 'react';
|
|
2
2
|
import { GalleryView } from '../client/gallery.js';
|
|
3
|
+
import { HlsLoader } from '../client/http.js';
|
|
3
4
|
import { AbrPolicy, PlaybackError } from '../client/playback.js';
|
|
4
5
|
import { PlayerTrack } from '../client/player.js';
|
|
5
6
|
export declare const GALLERY_VIEW_KEY = "ckui.media-gallery.view";
|
|
@@ -44,8 +45,8 @@ export type PlayerStatus = "idle" | "loading" | "playing" | "paused" | "bufferin
|
|
|
44
45
|
export interface HlsPlayerOptions {
|
|
45
46
|
/** The master playlist; nothing loads while absent. */
|
|
46
47
|
src?: string | null;
|
|
47
|
-
/**
|
|
48
|
-
|
|
48
|
+
/** Loads playlists with the app's credentials and prepares segment requests: the media client's `hls`. */
|
|
49
|
+
loader?: HlsLoader;
|
|
49
50
|
/**
|
|
50
51
|
* Re-grants access (refetch the read API). Called shortly before `expires`,
|
|
51
52
|
* and once after a 401, 403 or 404 — media-gateway answers an expired token
|
|
@@ -132,9 +133,11 @@ export interface UseHlsPlayer {
|
|
|
132
133
|
/** The selected subtitle track's TextTrack is kept "hidden": its cues are the caller's to draw. */
|
|
133
134
|
subtitles: PlayerTracks;
|
|
134
135
|
}
|
|
136
|
+
/** Fetches a playlist: the loader's, else a plain fetch. */
|
|
137
|
+
export declare const fetchPlaylist: (loader: HlsLoader | undefined, url: string, signal?: AbortSignal) => Promise<Response>;
|
|
135
138
|
/**
|
|
136
139
|
* HLS playback that fails fast with a reason: hls.js (lazy-loaded) or native
|
|
137
140
|
* HLS, tuned retries, a grant refresh before expiry and one on 401/403/404,
|
|
138
141
|
* and a stall watchdog.
|
|
139
142
|
*/
|
|
140
|
-
export declare function useHlsPlayer({ src,
|
|
143
|
+
export declare function useHlsPlayer({ src, loader, refresh, expires, active, stallTimeout, abr, qualityKey, startAt, autoPlay, audioLanguage, subtitleLanguage, volumeKey, trackKey, }: HlsPlayerOptions): UseHlsPlayer;
|