UNPKG

next-video

Version:

A React component for adding video to your Next.js application. It extends both the video element and your Next app with features for automatic video optimization.

140 lines (137 loc) 4.07 kB
"use client"; import { jsx, jsxs } from "react/jsx-runtime"; import { forwardRef, useState } from "react"; import DefaultPlayer from "./players/default-player.js"; import { Alert } from "./alert.js"; import { createVideoRequest, defaultLoader } from "./video-loader.js"; import * as transformers from "../providers/transformers.js"; import { config, camelCase, toSymlinkPath, usePolling, isReactComponent, getUrlExtension } from "./utils.js"; const NextVideo = forwardRef((props, forwardedRef) => { const DEV_MODE = true; let { as: VideoPlayer = DefaultPlayer, loader = defaultLoader, transform = defaultTransformer, className, style, src, width, height } = props; let [asset, setAsset] = useState(typeof src === "object" ? src : void 0); const [playing, setPlaying] = useState(false); if (typeof src === "object") { asset = src; src = void 0; } const loaderProps = { src, width, height }; const request = createVideoRequest(loader, loaderProps, (json) => setAsset(json)); const status = asset?.status; const fileExtension = getUrlExtension(src); const needsPolling = DEV_MODE && typeof src === "string" && status != "ready" && !["m3u8", "mpd"].includes(fileExtension ?? ""); usePolling(request, needsPolling ? 1e3 : null); const videoProps = getVideoProps({ ...props, transform, src }, { asset }); if (!isReactComponent(VideoPlayer)) { console.warn("The `as` property is not a valid component:", VideoPlayer); } return /* @__PURE__ */ jsxs("div", { className: `${className ? `${className} ` : ""}next-video-container`, style, children: [ /* @__PURE__ */ jsx("style", { /* css */ children: ` .next-video-container { display: grid; /* Fixes a Safari aspect-ratio + height bug. */ position: relative; width: 100%; aspect-ratio: 16 / 9; } [data-next-video] { display: block; position: relative; width: 100%; height: 100%; } [data-next-video] img { object-fit: var(--media-object-fit, contain); object-position: var(--media-object-position, center); max-width: 100%; max-height: 100%; min-width: 100%; min-height: 100%; } ` }), /* @__PURE__ */ jsx( VideoPlayer, { ref: forwardedRef, "data-next-video": status ?? "", style: { width, height }, asset, onPlaying: () => setPlaying(true), onPause: () => setPlaying(false), ...videoProps } ), DEV_MODE && /* @__PURE__ */ jsx(Alert, { hidden: Boolean(playing || !status || status === "ready"), status }) ] }); }); function getVideoProps(allProps, state) { const { asset } = state; const { controls = true, as, className, style, src, poster, blurDataURL, loader, transform, ...rest } = allProps; const props = { src, poster, controls, blurDataURL, ...rest }; if (typeof poster === "object") { props.poster = poster.src; props.blurDataURL ?? (props.blurDataURL = poster.blurDataURL); } if (asset) { if (asset.status === "ready") { props.blurDataURL ?? (props.blurDataURL = asset.blurDataURL); const transformedAsset = transform(asset, props); if (transformedAsset) { props.src = transformedAsset.sources?.[0]?.src; props.poster ?? (props.poster = transformedAsset.poster); props.thumbnailTime ?? (props.thumbnailTime = transformedAsset.thumbnailTime); } } else { props.src = toSymlinkPath(asset.originalFilePath); } } return props; } function defaultTransformer(asset, props) { const provider = asset.provider ?? config.provider; for (let [key, transformer] of Object.entries(transformers)) { if (key === camelCase(provider)) { return transformer.transform(asset, props); } } } var video_default = NextVideo; export { video_default as default, getVideoProps };