-
Notifications
You must be signed in to change notification settings - Fork 6
feat(video-player): migrate from react player to media-chrome #799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
40174e3
96ed2cf
1963385
f744741
9196e6f
c2ff0c2
d052e14
e26c1af
51bdeb3
fe646ea
476e8c6
1a3a9ac
109d6c7
5edcdf7
63a8ac5
6a31b7e
fae593c
5e5d77d
f57a308
fe985b8
8cbe01c
c7ce44a
4fdbcd2
5d12bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,25 +1,155 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useRef } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ReactPlayer from "react-player"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const VideoPlayer = ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isActive, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isActive: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useCallback, useEffect, useRef, useState } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaControlBar, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaController, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaFullscreenButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaMuteButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPipButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPlayButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPreviewChapterDisplay, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPreviewThumbnail, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPreviewTimeDisplay, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaTimeDisplay, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaTimeRange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaVolumeRange, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "media-chrome/react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { MediaProvider, useMediaRef } from "media-chrome/react/media-store"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPlaybackRateMenu, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MediaPlaybackRateMenuButton, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "media-chrome/react/menu"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FullscreenIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MuteIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PipIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PlayPauseIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SettingsIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "./video-player-icons"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONTROL_BAR_STYLE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CONTROLLER_STYLE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| YOUTUBE_CONTROLLER_STYLE, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "./video-player-theme"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import "./video-player-theme.css"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export interface VideoPlayerProps { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isYouTube?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError?: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const playerRef = useRef<HTMLVideoElement | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* ---- Main player ---- */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function VideoPlayerInner({ isYouTube, onError, src }: VideoPlayerProps) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mediaRef = useMediaRef(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onErrorRef = useRef(onError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onErrorRef.current = onError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const [youTubeReady, setYouTubeReady] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!isYouTube) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void (async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await import("youtube-video-element"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| setYouTubeReady(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isYouTube]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ref = useCallback( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (el: HTMLVideoElement | null) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mediaRef(el); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!el) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleError = () => onErrorRef.current?.(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| el.addEventListener("error", handleError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return () => el.removeEventListener("error", handleError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [mediaRef], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ref = useCallback( | |
| (el: HTMLVideoElement | null) => { | |
| mediaRef(el); | |
| if (!el) { | |
| return undefined; | |
| } | |
| const handleError = () => onErrorRef.current?.(); | |
| el.addEventListener("error", handleError); | |
| return () => el.removeEventListener("error", handleError); | |
| }, | |
| [mediaRef], | |
| ); | |
| const ref = useCallback( | |
| (el: HTMLVideoElement | null) => { | |
| mediaRef(el); | |
| if (!el) { | |
| return undefined; | |
| } | |
| const handleError = () => onErrorRef.current?.(); | |
| el.addEventListener("error", handleError); | |
| return () => { | |
| el.removeEventListener("error", handleError); | |
| mediaRef(null); | |
| }; | |
| }, | |
| [mediaRef], | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/VideoPlayer.tsx` around lines 63 - 76, The callback ref
returned from useCallback (the ref assigned to the video element) currently
returns a cleanup that only removes the error listener, which in React 19
prevents mediaRef(null) from being called on unmount and blocks MediaStore
unregister; update the ref callback (the function stored in ref) so its cleanup
both removes the "error" listener (handleError) and explicitly calls
mediaRef(null) when cleaning up, and ensure the initial branch that handles el
being null still returns undefined; keep using onErrorRef.current for the
handler reference and preserve the existing listener add/remove logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unhandled failure of
youtube-video-elementdynamic import — player silently becomes non-functional.If the dynamic import rejects (network error, etc.), the async IIFE throws inside
void (async () => {...})(), the rejection is swallowed,setYouTubeReady(true)is never called, and the user sees an empty/loadingMediaControllerindefinitely with no error feedback or fallback.🛠️ Proposed fix
🤖 Prompt for AI Agents