diff --git a/apps/docs/src/components/video/index.tsx b/apps/docs/src/components/video/index.tsx index 5c1ee37..3327a5f 100644 --- a/apps/docs/src/components/video/index.tsx +++ b/apps/docs/src/components/video/index.tsx @@ -1,7 +1,4 @@ "use client"; - -import type { ElementRef } from "react"; - import * as stylex from "@stylexjs/stylex"; import { MediaControlBar, @@ -20,7 +17,6 @@ import { MediaCaptionsMenu, MediaCaptionsMenuButton, } from "media-chrome/react/menu"; -import { useRef } from "react"; import type { StyleXComponentProps } from "../theme/types"; @@ -40,6 +36,18 @@ type VideoTrackKind = | "descriptions" | "metadata" | "subtitles"; +type VideoMediaTag = + | "cloudflare-video" + | "dash-video" + | "hls-video" + | "jwplayer-video" + | "mux-video" + | "shaka-video" + | "video" + | "video-js-video" + | "vimeo-video" + | "wistia-video" + | "youtube-video"; const styles = stylex.create({ root: { @@ -74,7 +82,7 @@ const styles = stylex.create({ "--media-control-padding": verticalSpace.lg, "--media-icon-color": uiColor.text1, "--media-primary-color": primaryColor.solid1, - "--media-range-track-background": uiColor.border1, + "--media-range-track-background": uiColor.border3, "--media-range-track-height": "4px", "--media-secondary-color": uiColor.bgSubtle, "--media-text-color": uiColor.text2, @@ -94,6 +102,73 @@ const styles = stylex.create({ }), }); +function getMediaTag(src?: string, mediaType?: VideoMediaTag): VideoMediaTag { + if (mediaType) { + return mediaType; + } + + if (!src) { + return "video"; + } + + const normalizedSrc = src.toLowerCase(); + + if ( + normalizedSrc.includes("iframe.videodelivery.net") || + (normalizedSrc.includes("customer-") && + normalizedSrc.includes(".cloudflarestream.com")) + ) { + return "cloudflare-video"; + } + + if (normalizedSrc.includes(".mpd")) { + return "dash-video"; + } + + if ( + normalizedSrc.includes(".m3u8") || + normalizedSrc.includes("getvideoplaylist") + ) { + return "hls-video"; + } + + if ( + normalizedSrc.includes("jwplayer.com") || + normalizedSrc.includes("jwplatform.com") + ) { + return "jwplayer-video"; + } + + if (normalizedSrc.includes("stream.mux.com")) { + return "mux-video"; + } + + if ( + normalizedSrc.includes("player.vimeo.com") || + normalizedSrc.includes("vimeo.com") + ) { + return "vimeo-video"; + } + + if ( + normalizedSrc.includes("fast.wistia.com") || + normalizedSrc.includes("wistia.com") || + normalizedSrc.includes("wistia.net") + ) { + return "wistia-video"; + } + + if ( + normalizedSrc.includes("youtube.com") || + normalizedSrc.includes("youtube-nocookie.com") || + normalizedSrc.includes("youtu.be") + ) { + return "youtube-video"; + } + + return "video"; +} + /** * A subtitle or caption track rendered as a native `` element. */ @@ -187,6 +262,12 @@ export interface VideoProps extends StyleXComponentProps< * Subtitle and caption tracks rendered inside the media element. */ subtitleTracks?: Array; + /** + * Override the Media Chrome media element used by the player. + * When omitted, the component will infer common providers from `src` + * and otherwise fall back to the native `video` element. + */ + mediaType?: VideoMediaTag; } export function Video({ @@ -196,26 +277,25 @@ export function Video({ seekOffset = DEFAULT_SEEK_OFFSET, style, aspectRatio = 16 / 9, + mediaType, subtitleTracks = [], ...props }: VideoProps) { const { src, ...videoProps } = props; - const controllerRef = useRef | null>(null); - const videoRef = useRef(null); const hasSubtitleTracks = subtitleTracks.length > 0; + const mediaTag = getMediaTag(src, mediaType); + const MediaTag = mediaTag as unknown as React.ElementType; return (
{/* oxlint-disable-next-line jsx_a11y/media-has-caption */} - + {hasSubtitleTracks ?