Skip to content

Instantly share code, notes, and snippets.

@akirco
Last active September 19, 2023 03:44
Show Gist options
  • Save akirco/bb75e3c1a10c8a03aff3eea9db1f2130 to your computer and use it in GitHub Desktop.
Save akirco/bb75e3c1a10c8a03aff3eea9db1f2130 to your computer and use it in GitHub Desktop.
use video.js play m3u8 stream
.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];
}
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;
'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