I am developing a videojs player using react, typescript. But I can’t get the video. It says unauthorized.
I already tried using vanilla way (without videojs) and it is working. I will state both tries below.
My versions:
video.js: "8.21.0",
@types/video.js: "7.3.58",
react: 18.3.1
// VideoPlayerMP4.tsx
import React, { useEffect, useRef } from 'react';
import videojs from 'video.js';
import Player from 'video.js/dist/types/player';
import 'video.js/dist/video-js.css';
type PlayerOptions = typeof videojs.options;
interface VideoPlayerProps {
id?: string;
url?: string;
options?: PlayerOptions;
styles?: string;
jwtToken: string;
}
const VideoPlayerMP4: React.FC<VideoPlayerProps> = ({
url,
options,
styles,
jwtToken,
}: VideoPlayerProps) => {
const videoRef = useRef<HTMLVideoElement | null>(null);
const playerRef = useRef<Player | null>();
useEffect(() => {
if (!options.sources.length || !videoRef.current) return;
const customOptions: PlayerOptions = {
...options,
html5: {
xhr: {
before: (xhr: XMLHttpRequest) => {
console.log('sent video token'); // i don't see this log
xhr.setRequestHeader('Authorization', `Bearer ${jwtToken}`);
},
},
},
};
console.log('sources', options);
if (!playerRef.current) {
const player = videojs(videoRef.current, customOptions, () => {
console.log('Video player is ready');
});
playerRef.current = player;
player.on('error', () => {});
player.on('loadedmetadata', () => {});
player.on('playing', () => {});
} else {
const currentSrc = playerRef.current.currentSrc();
const newSrc = customOptions.sources[0].src;
const player = playerRef.current;
if (currentSrc !== newSrc) {
player.autoplay(customOptions.autoplay);
player.src(customOptions.sources);
}
}
}, [url, options, jwtToken]);
return (
<div className={styles}>
<video
ref={videoRef}
className="video-js vjs-theme-city vjs-big-play-centered"
style={{ width: '100%', height: '100%' }}
></video>
</div>
);
};
export default VideoPlayerMP4;
// VideoPlayerVinilla.tsx
import React, { useEffect, useRef } from 'react';
interface VideoPlayerProps {
videoUrl: string;
jwtToken: string;
className?: string;
}
const VideoPlayerVanilla: React.FC<VideoPlayerProps> = ({
videoUrl,
jwtToken,
className,
}) => {
const videoRef = useRef<HTMLVideoElement | null>(null);
useEffect(() => {
const attachStream = () => {
if (!videoRef.current) return;
const xhr = new XMLHttpRequest();
xhr.open('GET', videoUrl, true);
xhr.setRequestHeader('Authorization', `Bearer ${jwtToken}`);
xhr.responseType = 'arraybuffer';
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
const videoBlob = new Blob([xhr.response], { type: 'video/mp4' });
const blobUrl = URL.createObjectURL(videoBlob);
videoRef.current!.src = blobUrl;
} else {
console.error('Failed to load video:', xhr.statusText);
}
};
xhr.onerror = () => {
console.error('An error occurred during the video request');
};
xhr.send();
};
attachStream();
return () => {
// Revoke blob URL if used
if (videoRef.current) {
URL.revokeObjectURL(videoRef.current.src);
videoRef.current.src = '';
}
};
}, [videoUrl, jwtToken]);
return (
<div className={className}>
<video
ref={videoRef}
controls
autoPlay
style={{ width: '100%', height: '100%' }}
>
Your browser does not support the video tag.
</video>
</div>
);
};
export default VideoPlayerVanilla;
Usage
<VideoPlayeMP4
url={videoDetails?.video_url || ''}
jwtToken={token}
options={{
autoplay: true,
controls: true,
muted: false,
preload: 'auto',
aspectRatio: '16:9',
fluid: true,
responsive: true,
poster: null,
sources: [
{
src: videoDetails?.video_url || '',
type: 'video/mp4',
},
],
}}
/>
<VideoPlayerVanilla
videoUrl={videoDetails?.video_url || ''}
jwtToken={token}
className="video-container"
/>
2
Answers
First, Add
console.log('customOptions:', customOptions)
to verify the options passed to video.jsSecond, Update
video.js
:maybe that will be helpful!
Video.js passes an MP4 directly to the video element to load. It doesn’t fetch it with XHR. The
beforeSend
etc options only apply to MSE playback of HLS and DASH.You could fetch the MP4 as you are doing in vanilla and pass the blob URL as a source to Video.js: