skip to Main Content

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


  1. First, Add console.log('customOptions:', customOptions) to verify the options passed to video.js

    Second, Update video.js:

    const customOptions: PlayerOptions = {
      ...options,
      html5: {
        ...options.html5,
        hls: {
          overrideNative: true, // Ensure the native behavior is overridden
        },
        xhr: {
          beforeSend: (xhr: XMLHttpRequest) => {
            console.log('Setting Authorization header');
            xhr.setRequestHeader('Authorization', `Bearer ${jwtToken}`);
          },
        },
      },
    };
    

    maybe that will be helpful!

    Login or Signup to reply.
  2. 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:

    player.src({ src: blobUrl, type: 'video/mp4' });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search