Last active
September 19, 2023 03:44
-
-
Save akirco/bb75e3c1a10c8a03aff3eea9db1f2130 to your computer and use it in GitHub Desktop.
use video.js play m3u8 stream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.vjs-control-bar { | |
@apply !bg-black/50 !h-[4rem] sm:!text-xl font-medium; | |
} | |
.vjs-control-text { | |
@apply !border-none; | |
} | |
.video-js .vjs-big-play-button { | |
@apply !rounded-full !h-[3rem] !bg-black/50 !border-none; | |
} | |
.vjs-button > .vjs-icon-placeholder:before { | |
@apply !relative; | |
} | |
.video-js .vjs-time-control { | |
@apply !flex !items-center; | |
} | |
.vjs-playback-rate .vjs-playback-rate-value { | |
@apply !leading-[4rem]; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useRef, useEffect, FC } from 'react'; | |
import videojs from 'video.js'; | |
import 'video.js/dist/video-js.css'; | |
import '@/styles/player.css'; | |
import type VPlayer from 'video.js/dist/types/player'; | |
interface VideoJSProps { | |
options: any; | |
onReady: (player: VPlayer) => void; | |
} | |
const Player: FC<VideoJSProps> = ({ options, onReady }) => { | |
const videoRef = useRef<HTMLDivElement | null>(null); | |
const playerRef = useRef<VPlayer | null>(null); | |
useEffect(() => { | |
if (!playerRef.current) { | |
const videoElement = document.createElement('video'); | |
videoElement.classList.add('vjs-big-play-centered'); | |
videoElement.classList.add('video-js'); | |
if (videoRef.current) { | |
videoRef.current.appendChild(videoElement); | |
} | |
const player = (playerRef.current = videojs(videoElement, options, () => { | |
onReady(player); | |
})); | |
} else { | |
const player = playerRef.current; | |
if (player) { | |
player.autoplay(options.autoplay); | |
player.src(options.sources); | |
} | |
} | |
}, [options, videoRef, onReady]); | |
useEffect(() => { | |
const player = playerRef.current; | |
return () => { | |
if (player && !player.isDisposed()) { | |
player.dispose(); | |
playerRef.current = null; | |
} | |
}; | |
}, []); | |
return ( | |
<div data-vjs-player className='w-full'> | |
<div ref={videoRef}></div> | |
</div> | |
); | |
}; | |
export default Player; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use client'; | |
import Player from '@/components/player'; | |
import localforage from 'localforage'; | |
import { useEffect, useRef, useState } from 'react'; | |
import type { StoragedVideo } from '@/types/video'; | |
import { Button } from '@/components/ui/button'; | |
import { Input } from '@/components/ui/input'; | |
import { PaperPlaneIcon } from '@radix-ui/react-icons'; | |
import type VPlayer from 'video.js/dist/types/player'; | |
import { | |
Card, | |
CardContent, | |
CardDescription, | |
CardTitle, | |
} from '@/components/ui/card'; | |
export default function Channel({ params }: { params: { id: string } }) { | |
const [currentPlay, setCurrentPlay] = useState<StoragedVideo>(); | |
const [current, setCurrent] = useState(0); | |
const playerRef = useRef<VPlayer | null>(null); | |
const videoJsOptions = { | |
autoplay: false, | |
controls: true, | |
responsive: true, | |
fluid: true, | |
playbackRates: [0.5, 1, 1.5, 2], | |
liveui: true, | |
html5: { | |
vhs: { | |
overrideNative: false, | |
useNetworkInformationApi: true, | |
experimentalBufferBasedABR: true, | |
}, | |
}, | |
controlBar: { | |
volumePanel: { | |
inline: false, | |
}, | |
}, | |
}; | |
const handlePlayerReady = (player: VPlayer) => { | |
playerRef.current = player; | |
player.on('waiting', () => { | |
player.log('player is waiting'); | |
}); | |
player.on('dispose', () => { | |
player.log('player will dispose'); | |
}); | |
}; | |
useEffect(() => { | |
if (params.id) { | |
localforage | |
.getItem<StoragedVideo>(params.id) | |
.then((res) => { | |
if (res) { | |
setCurrentPlay(res); | |
playerRef.current?.src([ | |
{ | |
type: 'application/x-mpegURL', | |
src: res.playUrls[current], | |
}, | |
]); | |
} | |
}) | |
.catch((err) => { | |
console.log(err); | |
}); | |
} | |
}, [current, params.id]); | |
return ( | |
<div className='flex flex-col xl:p-6 p-0'> | |
<main className='flex xl:gap-5 gap-0 w-full h-full xl:flex-row flex-col pb-16'> | |
<Player options={videoJsOptions} onReady={handlePlayerReady} /> | |
<Card className='flex flex-col xl:rounded-xl rounded-tl-none rounded-tr-none'> | |
<CardContent className='py-5 flex flex-col gap-5 justify-between h-full items-center'> | |
<h1 className='xl:text-2xl text-xl font-medium px-36 text-center'> | |
Chatbox | |
</h1> | |
<div className='flex w-full max-w-sm items-center space-x-2'> | |
<Input placeholder='Send a message?' type='search' /> | |
<Button size={'icon'}> | |
<PaperPlaneIcon /> | |
</Button> | |
</div> | |
</CardContent> | |
</Card> | |
</main> | |
<Card className='px-2 w-full'> | |
<CardContent className='py-5 flex flex-col gap-5'> | |
<CardTitle>Collections:</CardTitle> | |
<div className='flex flex-wrap gap-5'> | |
{currentPlay?.playUrls && currentPlay?.playUrls?.length > 0 | |
? currentPlay?.playUrls.map((url, index) => ( | |
<Button | |
key={index} | |
size={'icon'} | |
onClick={() => setCurrent(index)} | |
className={ | |
index === current | |
? 'text-orange-500 font-medium text-xl' | |
: '' | |
} | |
> | |
{index + 1} | |
</Button> | |
)) | |
: null} | |
</div> | |
<hr className='border-none w-full h-[2px] bg-neutral-100' /> | |
<CardTitle>{currentPlay?.vod_name}</CardTitle> | |
<CardTitle>Type:</CardTitle> | |
<CardDescription> | |
{currentPlay?.type_name} {currentPlay?.vod_class} | |
</CardDescription> | |
<CardTitle>Director:</CardTitle> | |
<CardDescription>{currentPlay?.vod_director}</CardDescription> | |
<CardTitle>Actor:</CardTitle> | |
<CardDescription>{currentPlay?.vod_actor}</CardDescription> | |
<CardTitle>Intro:</CardTitle> | |
<CardDescription>{currentPlay?.vod_blurb}...</CardDescription> | |
</CardContent> | |
</Card> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment