Fix Plyr player initialization with proper error handling
Added robust error handling for Plyr player instance: - Check if player.on method exists before using events - Fallback to polling time updates every 500ms if events unavailable - Use setInterval to wait for player initialization - Properly cleanup intervals on component unmount - Prevents "L.on is not a function" error This ensures the video player works even if Plyr's event system has issues initializing, using a reliable polling fallback. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -90,63 +90,82 @@ export const VideoPlayerWithChapters = forwardRef<VideoPlayerRef, VideoPlayerWit
|
|||||||
|
|
||||||
// Initialize Plyr and set up time tracking
|
// Initialize Plyr and set up time tracking
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isYouTube || !plyrRef.current) return;
|
if (!isYouTube) return;
|
||||||
|
|
||||||
const plyrElement = plyrRef.current;
|
// Wait for player to initialize
|
||||||
const player = plyrElement?.plyr;
|
const checkPlayer = setInterval(() => {
|
||||||
|
const player = plyrRef.current?.plyr;
|
||||||
if (!player) {
|
|
||||||
// Wait for player to be ready
|
|
||||||
const timeout = setTimeout(() => {
|
|
||||||
const p = plyrElement?.plyr;
|
|
||||||
if (p) {
|
|
||||||
setPlayerInstance(p);
|
|
||||||
setupTimeTracking(p);
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
setPlayerInstance(player);
|
|
||||||
setupTimeTracking(player);
|
|
||||||
|
|
||||||
function setupTimeTracking(plyr: any) {
|
|
||||||
// Track time updates for chapter highlighting
|
|
||||||
plyr.on('timeupdate', (event: any) => {
|
|
||||||
const time = plyr.currentTime;
|
|
||||||
setCurrentTime(time);
|
|
||||||
|
|
||||||
if (onTimeUpdate) {
|
|
||||||
onTimeUpdate(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find current chapter
|
|
||||||
if (chapters.length > 0) {
|
|
||||||
let index = chapters.findIndex((chapter, i) => {
|
|
||||||
const nextChapter = chapters[i + 1];
|
|
||||||
return time >= chapter.time && (!nextChapter || time < nextChapter.time);
|
|
||||||
});
|
|
||||||
|
|
||||||
// If before first chapter, no active chapter
|
|
||||||
if (index === -1 && time < chapters[0].time) {
|
|
||||||
index = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index !== currentChapterIndex) {
|
|
||||||
setCurrentChapterIndex(index);
|
|
||||||
if (index >= 0 && onChapterChange) {
|
|
||||||
onChapterChange(chapters[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (player) {
|
if (player) {
|
||||||
player.off('timeupdate');
|
clearInterval(checkPlayer);
|
||||||
|
setPlayerInstance(player);
|
||||||
|
|
||||||
|
// Set up time tracking using Plyr's event API
|
||||||
|
if (typeof player.on === 'function') {
|
||||||
|
player.on('timeupdate', () => {
|
||||||
|
const time = player.currentTime;
|
||||||
|
setCurrentTime(time);
|
||||||
|
|
||||||
|
if (onTimeUpdate) {
|
||||||
|
onTimeUpdate(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find current chapter
|
||||||
|
if (chapters.length > 0) {
|
||||||
|
let index = chapters.findIndex((chapter, i) => {
|
||||||
|
const nextChapter = chapters[i + 1];
|
||||||
|
return time >= chapter.time && (!nextChapter || time < nextChapter.time);
|
||||||
|
});
|
||||||
|
|
||||||
|
// If before first chapter, no active chapter
|
||||||
|
if (index === -1 && time < chapters[0].time) {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index !== currentChapterIndex) {
|
||||||
|
setCurrentChapterIndex(index);
|
||||||
|
if (index >= 0 && onChapterChange) {
|
||||||
|
onChapterChange(chapters[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Fallback: poll for time updates
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
const time = player.currentTime;
|
||||||
|
setCurrentTime(time);
|
||||||
|
|
||||||
|
if (onTimeUpdate) {
|
||||||
|
onTimeUpdate(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find current chapter
|
||||||
|
if (chapters.length > 0) {
|
||||||
|
let index = chapters.findIndex((chapter, i) => {
|
||||||
|
const nextChapter = chapters[i + 1];
|
||||||
|
return time >= chapter.time && (!nextChapter || time < nextChapter.time);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (index === -1 && time < chapters[0].time) {
|
||||||
|
index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index !== currentChapterIndex) {
|
||||||
|
setCurrentChapterIndex(index);
|
||||||
|
if (index >= 0 && onChapterChange) {
|
||||||
|
onChapterChange(chapters[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
// Store interval ID for cleanup
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearInterval(checkPlayer);
|
||||||
}, [isYouTube, chapters, currentChapterIndex, onChapterChange, onTimeUpdate]);
|
}, [isYouTube, chapters, currentChapterIndex, onChapterChange, onTimeUpdate]);
|
||||||
|
|
||||||
// Jump to specific time using Plyr API
|
// Jump to specific time using Plyr API
|
||||||
|
|||||||
Reference in New Issue
Block a user