reactjsaudionextjs-15vidstack-player

Getting error media is not ready - wait for can-play event on trying to play audio


I'm working with vidstack/react to play audio on an external button click. I'm trying to set the src prop dynamically inside a React component and play the audio, but I'm getting the following error:

Error: Error: [vidstack] media is not ready - wait for can-play event.

Here's the relevant code I have so far:

import '@vidstack/react/player/styles/default/theme.css';
import '@vidstack/react/player/styles/default/layouts/audio.css';

import { useEffect, useRef, useState } from 'react';

import {
  MediaPlayer,
  MediaProvider,
  type MediaPlayerInstance,
} from '@vidstack/react';

import {
  DefaultAudioLayout,
  defaultLayoutIcons,
} from '@vidstack/react/player/layouts/default';
import { playbackService } from './playbackService';

export function Player() {
  const [src, setSrc] = useState('');
  const playerRef = useRef<MediaPlayerInstance>(null);

  const handleClick = () => {
    playbackService.playSong();
  };

  const initPlaybackRelatedServices = () => {
    const playerInstance = playerRef.current;

    if (playerInstance) {
      playbackService.init({ media: playerInstance });
    }
  };

  useEffect(() => {
    if (playerRef.current) {
      console.log('set player instance initPlaybackRelatedServices');
      initPlaybackRelatedServices();
    }
  }, []);

  return (
    <>
      <button onClick={handleClick}>Click to play audio</button>
      <MediaPlayer title="" viewType="audio" src="" ref={playerRef}>
        <MediaProvider />
        <DefaultAudioLayout icons={defaultLayoutIcons} />
      </MediaPlayer>
    </>
  );
}

playerService

import type { MediaPlayerInstance } from '@vidstack/react';

interface AudioPlayerProvider {
  media: MediaPlayerInstance;
}

class PlayerService {
  private provider: AudioPlayerProvider | null = null;

  public playSong() {
    if (this.provider && this.provider.media) {
      this.provider.media.src =
        'https://521dimensions.com/song/FirstSnow-Emancipator.mp3';
      this.provider?.media?.play();
      console.log('Waiting for media to be ready...');
    } else {
      console.log('Player or media instance is not available.');
    }
  }

  public init(provider: AudioPlayerProvider) {
    this.provider = provider;
    console.log('AudioPlayerService initialized with player:', provider.media);
   
  }

  public getMedia() {
    return this.provider?.media;
  }

  public play() {
    this.provider?.media.play();
  }

  public pause() {
    this.provider?.media.pause();
  }
}

export const playbackService = new PlayerService();

I want to set the src dynamically through a service (playbackService) when the button is clicked. The error suggests that the media is not ready to be played when I set the src value.

How can I ensure that the media is ready before I set the src and start playback?

I am referring the given documentation but not able to solve this. what I am missing here?

https://vidstack.io/docs/player/components/core/player/?styling=default-theme

stackblitz implementation: https://stackblitz.com/edit/vidstack-examples-lhgzb5cc

I also tried to wait for can-play event but it doesn't fire.

public playSong() {
    if (this.provider && this.provider.media) {
        const media = this.provider.media;

        media.src = 'https://521dimensions.com/song/FirstSnow-Emancipator.mp3';

        const handleCanPlay = () => {
            console.log('Media is ready, playing now.');
            media.play();
            media.removeEventListener('can-play', handleCanPlay); // Clean up
        };

        media.addEventListener('can-play', handleCanPlay);
    } else {
        console.log('Player or media instance is not available.');
    }
}

Solution

  • I was able to solve this by using MediaSrc object provided by the library.

    const [src, setSrc] = useState<MediaSrc>('');

    on handleClick method I set the src using:

      const handleClick = () => {
        if (playerRef.current) {
          setSrc('https://521dimensions.com/song/FirstSnow-Emancipator.mp3');
        }
      };
    
    

    and updated the MediaPlayercomponent to:

    <MediaPlayer
        title=""
        viewType="audio"
        src={src}
        ref={playerRef}
        onCanPlay={onCanPlay}
      >
        <MediaProvider />
        <DefaultAudioLayout icons={defaultLayoutIcons} />
      </MediaPlayer>