Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix custom player types + add react-player example #242

Merged
merged 3 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export default function Page() {
```


### Custom Player
### Custom Player ([Demo](https://next-video-demo.vercel.app/custom-player))

You can customize the player by passing a custom player component to the `as` prop.
The custom player component accepts the following props:
Expand All @@ -254,23 +254,32 @@ The custom player component accepts the following props:

```tsx
import Video from 'next-video';
import { ReactPlayerAsVideo } from './player';
import ReactPlayer from './player';
import awesomeVideo from '/videos/awesome-video.mp4';

export default function Page() {
return <Video as={ReactPlayerAsVideo} src={awesomeVideo} />;
return <Video as={ReactPlayer} src={awesomeVideo} />;
}
```

```tsx
// player.js
// player.tsx
'use client';

import type { PlayerProps } from 'next-video';
import ReactPlayer from 'react-player';

export function ReactPlayerAsVideo(props) {
let { asset, src, poster, blurDataURL, ...rest } = props;
export default function Player(props: PlayerProps) {
let { asset, src, poster, blurDataURL, thumbnailTime, ...rest } = props;
let config = { file: { attributes: { poster } } };

return <ReactPlayer url={src} config={config} {...rest} />;
return <ReactPlayer
url={src}
config={config}
width="100%"
height="100%"
{...rest}
/>;
}
```

Expand Down
18 changes: 18 additions & 0 deletions examples/default-provider/app/custom-player/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Metadata } from 'next';
import Video from 'next-video';
import ReactPlayer from './player'
import getStarted from '/videos/country-clouds.mp4';

export const metadata: Metadata = {
title: 'next-video - Custom Player',
};

export default function Page() {
return (
<>
<section>
<Video as={ReactPlayer} src={getStarted} />
</section>
</>
);
}
17 changes: 17 additions & 0 deletions examples/default-provider/app/custom-player/player.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use client';

import type { PlayerProps } from 'next-video';
import ReactPlayer from 'react-player';

export default function Player(props: PlayerProps) {
let { asset, src, poster, blurDataURL, thumbnailTime, ...rest } = props;
let config = { file: { attributes: { poster } } };

return <ReactPlayer
url={src}
config={config}
width="100%"
height="100%"
{...rest}
/>;
}
7 changes: 5 additions & 2 deletions examples/default-provider/app/sidebar-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ export default function SidebarNav() {
<Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">Basic example</Link>
</li>
<li>
<Link className={`link ${pathname === '/background-video' ? 'active' : ''}`} href="/background-video">Background Video</Link>
<Link className={`link ${pathname === '/background-video' ? 'active' : ''}`} href="/background-video">Background video</Link>
</li>
<li>
<Link className={`link ${pathname === '/slotted-poster' ? 'active' : ''}`} href="/slotted-poster">Slotted Poster</Link>
<Link className={`link ${pathname === '/custom-player' ? 'active' : ''}`} href="/custom-player">Custom player</Link>
</li>
<li>
<Link className={`link ${pathname === '/slotted-poster' ? 'active' : ''}`} href="/slotted-poster">Slotted poster</Link>
</li>
<li>
<Link className={`link ${pathname === '/string-source' ? 'active' : ''}`} href="/string-source">String video source</Link>
Expand Down
48 changes: 42 additions & 6 deletions examples/default-provider/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion examples/default-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"next-video": "file:../..",
"open-props": "^1.7.3",
"react": "^18",
"react-dom": "^18"
"react-dom": "^18",
"react-player": "^3.0.0-canary.0"
},
"devDependencies": {
"@types/node": "^20",
Expand Down
15 changes: 3 additions & 12 deletions src/components/players/background-player.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,8 @@ import { svgBlurImage } from '../utils.js';
export type BackgroundPlayerProps = Omit<DefaultPlayerProps, 'src'>;

export const BackgroundPlayer = forwardRef((allProps: BackgroundPlayerProps, forwardedRef: any) => {
let {
style,
children,
asset,
controls,
poster,
blurDataURL,
onPlaying,
onLoadStart,
...rest
} = allProps;
let { style, children, asset, controls, poster, blurDataURL, onPlaying, onLoadStart, ...rest } =
allProps;

const slottedPoster = Children.toArray(children).find((child) => {
return typeof child === 'object' && 'type' in child && child.props.slot === 'poster';
Expand Down Expand Up @@ -75,7 +66,7 @@ export const BackgroundPlayer = forwardRef((allProps: BackgroundPlayerProps, for
}

// Remove props that are not supported by MuxVideo.
delete props.thumbnailTime
delete props.thumbnailTime;

const [posterHidden, setPosterHidden] = useState(false);

Expand Down
10 changes: 10 additions & 0 deletions src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,19 @@ export interface PlayerProps {
*/
controls?: boolean;

/**
* The poster image for the video.
*/
poster?: StaticImageData | string;

/**
* Set a manual data URL to be used as a placeholder image before the poster image successfully loads.
* For imported videos this will be automatically generated.
*/
blurDataURL?: string;

/**
* The thumbnail time in seconds to use for the video poster image.
*/
thumbnailTime?: number;
}
38 changes: 17 additions & 21 deletions src/components/video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ 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 { config, camelCase, toSymlinkPath, usePolling, isReactComponent, getUrlExtension } from './utils.js';
import * as transformers from '../providers/transformers.js';
import {
config,
camelCase,
toSymlinkPath,
usePolling,
isReactComponent,
getUrlExtension,
} from './utils.js';

import type { DefaultPlayerProps } from './players/default-player.js';
import type { Asset } from '../assets.js';
import type { VideoLoaderProps, VideoProps, VideoPropsInternal } from './types.js';
import type { VideoLoaderProps, VideoProps, VideoPropsInternal, PlayerProps } from './types.js';

const NextVideo = forwardRef((props: VideoProps, forwardedRef) => {
// Keep in component so we can emulate the DEV_MODE.
Expand Down Expand Up @@ -52,22 +59,16 @@ const NextVideo = forwardRef((props: VideoProps, forwardedRef) => {

usePolling(request, needsPolling ? 1000 : null);

const videoProps = getVideoProps(
{ ...props, transform, src } as VideoPropsInternal,
{ asset }
);
const videoProps = getVideoProps({ ...props, transform, src } as VideoPropsInternal, { asset });

if (!isReactComponent(VideoPlayer)) {
console.warn('The `as` property is not a valid component:', VideoPlayer);
}

return (
<div
className={`${className ? `${className} ` : ''}next-video-container`}
style={style}
>
<div className={`${className ? `${className} ` : ''}next-video-container`} style={style}>
<style>{
/* css */`
/* css */ `
.next-video-container {
position: relative;
width: 100%;
Expand Down Expand Up @@ -102,10 +103,9 @@ const NextVideo = forwardRef((props: VideoProps, forwardedRef) => {
{...videoProps}
></VideoPlayer>

{DEV_MODE && <Alert
hidden={Boolean(playing || !status || status === 'ready')}
status={status}
/>}
{DEV_MODE && (
<Alert hidden={Boolean(playing || !status || status === 'ready')} status={status} />
)}
</div>
);
});
Expand All @@ -130,7 +130,7 @@ export function getVideoProps(allProps: VideoPropsInternal, state: { asset?: Ass
src: src as string | undefined,
controls,
blurDataURL,
...rest
...rest,
};

// Handle StaticImageData which are image imports that resolve to an object.
Expand Down Expand Up @@ -171,8 +171,4 @@ function defaultTransformer(asset: Asset, props: Record<string, any>) {

export default NextVideo;

export type {
VideoLoaderProps,
VideoProps,
DefaultPlayerProps,
};
export type { VideoLoaderProps, VideoProps, DefaultPlayerProps, PlayerProps };