-
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 4 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,151 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import React, { useRef } from "react"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import ReactPlayer from "react-player"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const VideoPlayer = ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isActive, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useCallback, useEffect, useRef } 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 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isActive: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isYouTube?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError?: () => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const playerRef = useRef<HTMLVideoElement | null>(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* ---- Main player ---- */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function VideoPlayerInner({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isYouTube, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: Omit<VideoPlayerProps, "isActive">) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const mediaRef = useMediaRef(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onErrorRef = useRef(onError); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onErrorRef.current = onError; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isYouTube) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| void import("youtube-video-element"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, [isYouTube]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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.
Check warning on line 98 in src/components/VideoPlayer.tsx
GitHub Actions / spellcheck
Unknown word (notooltip)
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { Icon } from "@/components/ui/recollect/icon"; | ||
|
|
||
| export function PlayPauseIcon() { | ||
| return ( | ||
| <Icon slot="icon" viewBox="0 0 24 24"> | ||
| <g className="icon-state icon-play"> | ||
| <path | ||
| d="M8.04074 2.32259C6.70797 1.51044 5 2.46975 5 4.03047V19.9696C5 21.5304 6.70798 22.4897 8.04074 21.6775L21.119 13.7079C22.398 12.9285 22.398 11.0716 21.119 10.2922L8.04074 2.32259Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| <g className="icon-state icon-pause"> | ||
| <path | ||
| d="M4 5C4 3.89543 4.89543 3 6 3H8C9.10457 3 10 3.89543 10 5V19C10 20.1046 9.10457 21 8 21H6C4.89543 21 4 20.1046 4 19V5Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M14 5C14 3.89543 14.8954 3 16 3H18C19.1046 3 20 3.89543 20 5V19C20 20.1046 19.1046 21 18 21H16C14.8954 21 14 20.1046 14 19V5Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| </Icon> | ||
| ); | ||
| } | ||
|
|
||
| function VolumeOnIcon() { | ||
| return ( | ||
| <g className="icon-state vol-on"> | ||
| <path | ||
| d="M12.4719 3.11833C12.797 3.29235 13 3.63121 13 4V20C13 20.3688 12.797 20.7077 12.4719 20.8817C12.1467 21.0557 11.7522 21.0366 11.4453 20.8321L5.69722 17H3C1.89543 17 1 16.1046 1 15V9C1 7.89543 1.89543 7 3 7H5.69722L11.4453 3.16795C11.7522 2.96338 12.1467 2.94431 12.4719 3.11833Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M16.5963 7.40422C16.2057 7.0137 15.5726 7.0137 15.1821 7.40422C14.7915 7.79475 14.7915 8.42791 15.1821 8.81843C15.9975 9.63387 16.5001 10.7575 16.5001 12.0004C16.5001 13.2433 15.9975 14.367 15.1821 15.1824C14.7915 15.5729 14.7915 16.2061 15.1821 16.5966C15.5726 16.9871 16.2057 16.9871 16.5963 16.5966C17.7714 15.4214 18.5001 13.7951 18.5001 12.0004C18.5001 10.2058 17.7714 8.5794 16.5963 7.40422Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| ); | ||
| } | ||
|
|
||
| function VolumeOffIcon() { | ||
| return ( | ||
| <g className="icon-state vol-off"> | ||
| <path | ||
| d="M20.7071 4.70711C21.0976 4.31658 21.0976 3.68342 20.7071 3.29289C20.3166 2.90237 19.6834 2.90237 19.2929 3.29289L17 5.58579V4C17 3.63121 16.797 3.29235 16.4719 3.11833C16.1467 2.94431 15.7522 2.96338 15.4453 3.16795L9.69722 7H7C5.89543 7 5 7.89543 5 9V15C5 15.7021 5.36182 16.3198 5.90917 16.6766L3.29289 19.2929C2.90237 19.6834 2.90237 20.3166 3.29289 20.7071C3.68342 21.0976 4.31658 21.0976 4.70711 20.7071L20.7071 4.70711Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M10.6246 17.6183L17 11.2429V20C17 20.3688 16.797 20.7077 16.4719 20.8817C16.1467 21.0557 15.7522 21.0366 15.4453 20.8321L10.6246 17.6183Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| ); | ||
| } | ||
|
|
||
| export function MuteIcon() { | ||
| return ( | ||
| <Icon slot="icon" viewBox="0 0 24 24"> | ||
| <VolumeOnIcon /> | ||
| <VolumeOffIcon /> | ||
| </Icon> | ||
| ); | ||
| } | ||
|
|
||
| export function SettingsIcon(props?: { slotIcon?: boolean }) { | ||
| return ( | ||
| <Icon | ||
| {...(props?.slotIcon !== false && { slot: "icon" })} | ||
| viewBox="0 0 24 24" | ||
| > | ||
| <path | ||
| d="M12 2C11.4477 2 11 2.44772 11 3V5C11 5.55228 11.4477 6 12 6C12.5523 6 13 5.55228 13 5V4.06189C16.9463 4.55399 20 7.92038 20 12C20 16.4183 16.4183 20 12 20C7.58172 20 4 16.4183 4 12C4 11.0632 4.16061 10.166 4.45496 9.33325C4.639 8.81253 4.36608 8.24121 3.84536 8.05716C3.32465 7.87311 2.75332 8.14604 2.56928 8.66675C2.20032 9.71063 2 10.833 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M5.04289 5.04289C5.43342 4.65237 6.06658 4.65237 6.45711 5.04289L10.3349 8.92069C10.8301 8.65237 11.3972 8.5 12 8.5C13.933 8.5 15.5 10.067 15.5 12C15.5 13.933 13.933 15.5 12 15.5C10.067 15.5 8.5 13.933 8.5 12C8.5 11.3972 8.65237 10.8301 8.92069 10.3349L5.04289 6.45711C4.65237 6.06658 4.65237 5.43342 5.04289 5.04289Z" | ||
| fill="currentColor" | ||
| /> | ||
| </Icon> | ||
| ); | ||
| } | ||
|
|
||
| export function PipIcon() { | ||
| return ( | ||
| <Icon slot="icon" viewBox="0 0 24 24"> | ||
| <path | ||
| d="M4 4C2.89543 4 2 4.89543 2 6V16C2 17.1046 2.89543 18 4 18H9C9.55228 18 10 17.5523 10 17C10 16.4477 9.55228 16 9 16H4V6L18 6V11C18 11.5523 18.4477 12 19 12C19.5523 12 20 11.5523 20 11V6C20 4.89543 19.1046 4 18 4H4Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| d="M13.5 14C12.6716 14 12 14.6716 12 15.5V19.5C12 20.3284 12.6716 21 13.5 21H20.5C21.3284 21 22 20.3284 22 19.5V15.5C22 14.6716 21.3284 14 20.5 14H13.5Z" | ||
| fill="currentColor" | ||
| /> | ||
| </Icon> | ||
| ); | ||
| } | ||
|
|
||
| function FullscreenEnterIcon() { | ||
| return ( | ||
| <g className="icon-state fs-enter"> | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M3 5C3 3.89543 3.89543 3 5 3H8C8.55228 3 9 3.44772 9 4C9 4.55228 8.55228 5 8 5H5V8C5 8.55228 4.55228 9 4 9C3.44772 9 3 8.55228 3 8V5ZM15 4C15 3.44772 15.4477 3 16 3H19C20.1046 3 21 3.89543 21 5V8C21 8.55228 20.5523 9 20 9C19.4477 9 19 8.55228 19 8V5H16C15.4477 5 15 4.55228 15 4ZM4 15C4.55228 15 5 15.4477 5 16V19H8C8.55228 19 9 19.4477 9 20C9 20.5523 8.55228 21 8 21H5C3.89543 21 3 20.1046 3 19V16C3 15.4477 3.44772 15 4 15ZM20 15C20.5523 15 21 15.4477 21 16V19C21 20.1046 20.1046 21 19 21H16C15.4477 21 15 20.5523 15 20C15 19.4477 15.4477 19 16 19H19V16C19 15.4477 19.4477 15 20 15Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| ); | ||
| } | ||
|
|
||
| function FullscreenExitIcon() { | ||
| return ( | ||
| <g className="icon-state fs-exit"> | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M10 8C10 9.10457 9.10457 10 8 10H4C3.44772 10 3 9.55228 3 9C3 8.44772 3.44772 8 4 8H8V4C8 3.44772 8.44772 3 9 3C9.55228 3 10 3.44772 10 4V8Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M21 9C21 9.55228 20.5523 10 20 10H16C14.8954 10 14 9.10457 14 8V4C14 3.44772 14.4477 3 15 3C15.5523 3 16 3.44772 16 4V8H20C20.5523 8 21 8.44772 21 9Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M15 21C14.4477 21 14 20.5523 14 20V16C14 14.8954 14.8954 14 16 14H20C20.5523 14 21 14.4477 21 15C21 15.5523 20.5523 16 20 16H16V20C16 20.5523 15.5523 21 15 21Z" | ||
| fill="currentColor" | ||
| /> | ||
| <path | ||
| fillRule="evenodd" | ||
| clipRule="evenodd" | ||
| d="M9 21C8.44772 21 8 20.5523 8 20V16H4C3.44772 16 3 15.5523 3 15C3 14.4477 3.44772 14 4 14H8C9.10457 14 10 14.8954 10 16V20C10 20.5523 9.55228 21 9 21Z" | ||
| fill="currentColor" | ||
| /> | ||
| </g> | ||
| ); | ||
| } | ||
|
|
||
| export function FullscreenIcon() { | ||
| return ( | ||
| <Icon slot="icon" viewBox="0 0 24 24"> | ||
| <FullscreenEnterIcon /> | ||
| <FullscreenExitIcon /> | ||
| </Icon> | ||
| ); | ||
| } |
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.
🧩 Analysis chain
🏁 Script executed:
Repository: timelessco/recollect
Length of output: 334
🏁 Script executed:
Repository: timelessco/recollect
Length of output: 1220
🌐 Web query:
media-chrome 4.18.0 release notes changelog💡 Result:
media-chrome 4.18.0 — changelog (released 2026-02-19)
Features
Bug fixes
aria-labelfixes for time range and seek buttons (PR #1253) [1]Upstream compare link:
v4.17.2...v4.18.0[1]Update
media-chrometo4.18.0. The current version4.17.2is outdated. The latest stable release includes memory leak patches, accessibility improvements, and bug fixes.youtube-video-element@1.9.0is current.🤖 Prompt for AI Agents