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>
</>
);
}
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.');
}
}
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 MediaPlayer
component to:
<MediaPlayer
title=""
viewType="audio"
src={src}
ref={playerRef}
onCanPlay={onCanPlay}
>
<MediaProvider />
<DefaultAudioLayout icons={defaultLayoutIcons} />
</MediaPlayer>