87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
/**
|
|
* Main Video Player Component
|
|
* Based on deobfuscated TikTok video player implementation
|
|
*/
|
|
|
|
import React, { useRef, useEffect } from 'react';
|
|
import { VideoItem } from '../types';
|
|
import { MediaPlayer, MediaProvider, type MediaPlayerInstance } from '@vidstack/react';
|
|
import { defaultLayoutIcons, DefaultVideoLayout } from '@vidstack/react/player/layouts/default';
|
|
|
|
// Import Vidstack styles
|
|
import '@vidstack/react/player/styles/default/theme.css';
|
|
import '@vidstack/react/player/styles/default/layouts/video.css';
|
|
|
|
interface VideoPlayerProps {
|
|
video: VideoItem;
|
|
isActive: boolean;
|
|
isMuted: boolean;
|
|
autoPlay: boolean;
|
|
onPlayerChange: (player: MediaPlayerInstance | null) => void;
|
|
className?: string;
|
|
}
|
|
|
|
export const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
|
video,
|
|
isActive,
|
|
isMuted,
|
|
autoPlay,
|
|
onPlayerChange,
|
|
className = ''
|
|
}) => {
|
|
const player = useRef<MediaPlayerInstance>(null);
|
|
console.log('autoPlay ' + video.video.playAddr, autoPlay, video);
|
|
|
|
useEffect(() => {
|
|
// player.current.muted = false;
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
// Pass the player instance to the parent component.
|
|
onPlayerChange(player.current);
|
|
}, [onPlayerChange]);
|
|
|
|
useEffect(() => {
|
|
// This effect ensures that the mute state of the player is always in sync
|
|
// with the global mute state managed by the VideoFeed component.
|
|
if (player.current) {
|
|
player.current.muted = isMuted;
|
|
}
|
|
}, [isMuted]);
|
|
|
|
return (
|
|
<div className={`w-full h-full bg-black flex justify-center items-center ${className}`}>
|
|
{/* Video Player - Using Vidstack for HLS support */}
|
|
<MediaPlayer
|
|
ref={player}
|
|
title={video.desc || 'Video'}
|
|
src={
|
|
video.video.playAddr.includes('/api/videos/')
|
|
? { src: video.video.playAddr, type: 'video/mp4' }
|
|
: video.video.playAddr
|
|
}
|
|
poster={video.video.cover}
|
|
playsInline
|
|
loop
|
|
muted={false}
|
|
autoPlay={true}
|
|
load={isActive ? "eager" : "idle"}
|
|
posterLoad="eager"
|
|
crossOrigin="anonymous"
|
|
className="w-full h-full"
|
|
style={{
|
|
'--video-brand': '#ff0050',
|
|
'--media-object-fit': 'contain',
|
|
'--media-object-position': 'center'
|
|
} as any}
|
|
>
|
|
<MediaProvider />
|
|
<DefaultVideoLayout
|
|
icons={defaultLayoutIcons}
|
|
noScrubGesture
|
|
/>
|
|
</MediaPlayer>
|
|
</div>
|
|
);
|
|
};
|