1- import { useRef , useEffect , useState } from "react" ;
1+ import { useRef , useEffect , useState , useCallback } from "react" ;
22import { usePlayerStore } from "../../stores/playerStore" ;
33import { toast } from "react-hot-toast" ;
44import { Play , Pause } from "lucide-react" ;
@@ -13,8 +13,9 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
1313 const videoRef = useRef < HTMLVideoElement > ( null ) ;
1414 const [ localPlayState , setLocalPlayState ] = useState ( false ) ;
1515
16-
1716 const isDelayingRef = useRef ( false ) ;
17+ // Track pending play intent so we can start playback once the element is ready
18+ const pendingPlayRef = useRef ( false ) ;
1819
1920 const {
2021 currentFile,
@@ -38,6 +39,69 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
3839 setLocalPlayState ( isPlaying ) ;
3940 } , [ isPlaying ] ) ;
4041
42+ // Helper to safely play a media element
43+ const safePlay = useCallback (
44+ ( mediaElement : HTMLMediaElement ) => {
45+ // readyState >= 2 (HAVE_CURRENT_DATA) means enough data to play
46+ if ( mediaElement . readyState >= 2 ) {
47+ mediaElement . play ( ) . catch ( ( err ) => {
48+ console . error ( "Error playing media:" , err ) ;
49+ toast . error (
50+ "Error playing media. The file may be corrupted or not supported."
51+ ) ;
52+ setIsPlaying ( false ) ;
53+ } ) ;
54+ } else {
55+ // Not ready yet – mark as pending and wait for canplay
56+ pendingPlayRef . current = true ;
57+ }
58+ } ,
59+ [ setIsPlaying ]
60+ ) ;
61+
62+ // Reset pending play when the media source changes
63+ useEffect ( ( ) => {
64+ pendingPlayRef . current = false ;
65+ } , [ currentFile ?. url ] ) ;
66+
67+ // Listen for canplay to know when the element is ready
68+ useEffect ( ( ) => {
69+ const mediaElement = currentFile ?. type . includes ( "video" )
70+ ? videoRef . current
71+ : audioRef . current ;
72+ if ( ! mediaElement ) return ;
73+
74+ const handleCanPlay = ( ) => {
75+ // If a play was requested while we were loading, start now
76+ if ( pendingPlayRef . current ) {
77+ pendingPlayRef . current = false ;
78+ mediaElement . play ( ) . catch ( ( err ) => {
79+ console . error ( "Error playing media after canplay:" , err ) ;
80+ setIsPlaying ( false ) ;
81+ } ) ;
82+ }
83+ } ;
84+
85+ mediaElement . addEventListener ( "canplay" , handleCanPlay ) ;
86+ // If it's already ready (cached / fast load), handle pending play immediately
87+ if ( mediaElement . readyState >= 2 ) {
88+ handleCanPlay ( ) ;
89+ }
90+ return ( ) => {
91+ mediaElement . removeEventListener ( "canplay" , handleCanPlay ) ;
92+ } ;
93+ } , [ currentFile , setIsPlaying ] ) ;
94+
95+ // Pause playback when the component unmounts so the store stays in sync
96+ useEffect ( ( ) => {
97+ return ( ) => {
98+ const { isPlaying : stillPlaying } = usePlayerStore . getState ( ) ;
99+ if ( stillPlaying ) {
100+ usePlayerStore . getState ( ) . setIsPlaying ( false ) ;
101+ }
102+ } ;
103+ } , [ ] ) ;
104+
41105 // Handle play/pause
42106 useEffect ( ( ) => {
43107 const mediaElement = currentFile ?. type . includes ( "video" )
@@ -47,18 +111,12 @@ export const MediaPlayer = ({ hiddenMode = false }: MediaPlayerProps) => {
47111
48112 if ( isPlaying ) {
49113 if ( isDelayingRef . current ) return ; // Don't interfere if delaying
50- console . log ( "Attempting to play media:" , currentFile ?. url ) ;
51- mediaElement . play ( ) . catch ( ( err ) => {
52- console . error ( "Error playing media:" , err ) ;
53- toast . error (
54- "Error playing media. The file may be corrupted or not supported."
55- ) ;
56- setIsPlaying ( false ) ;
57- } ) ;
114+ safePlay ( mediaElement ) ;
58115 } else {
116+ pendingPlayRef . current = false ;
59117 mediaElement . pause ( ) ;
60118 }
61- } , [ isPlaying , currentFile , setIsPlaying ] ) ;
119+ } , [ isPlaying , currentFile , setIsPlaying , safePlay ] ) ;
62120
63121 // Handle volume changes
64122 useEffect ( ( ) => {
0 commit comments