Skip to content

(video) feature: unified video player interface with YouTube IFrame API integration#2421

Closed
nwizugbesamson wants to merge 3 commits intoIvy-Interactive:mainfrom
nwizugbesamson:feature/2420-video-proposed-interface-and-abstraction-tsx
Closed

(video) feature: unified video player interface with YouTube IFrame API integration#2421
nwizugbesamson wants to merge 3 commits intoIvy-Interactive:mainfrom
nwizugbesamson:feature/2420-video-proposed-interface-and-abstraction-tsx

Conversation

@nwizugbesamson
Copy link
Copy Markdown
Contributor

@nwizugbesamson nwizugbesamson commented Feb 28, 2026

Summary

This PR refactors the VideoPlayer widget's frontend implementation to establish a shared props interface across all player backends and introduce the YouTube IFrame API as the rendering layer for YouTube videos — replacing the previous <iframe> approach and laying the foundation for imperative prop controls (e.g. volume, seek).

Why

The old implementation had no shared contract between native and YouTube player components. Each player received ad-hoc props with no type enforcement, and YouTube videos were rendered via a static <iframe> with no access to the player's JS API. That meant properties like muted, loop, controls, and autoplay had to be baked into the embed URL — and future controls (volume, playback rate, etc.) would be impossible without a full rewrite.

What changed

Shared VideoPlayerProps interface (video/types.ts)

All player backends now receive the same typed props:

export interface VideoPlayerProps {
  id: string;
  validatedSrc: string;
  source: string;
  width?: string;
  height?: string;
  autoplay?: boolean;
  loop?: boolean;
  muted?: boolean;
  controls?: boolean;
  poster?: string | null;
  onError?: () => void;
}

Player registry (players.ts)

New players are registered in a typed map. Adding a new backend is a one-liner:

export const PLAYERS: Record<VideoType, ComponentType<VideoPlayerProps>> = {
  native: NativeVideoPlayer,
  youtube: YouTubePlayer,
};

YouTube IFrame API loader (lib/youtube-api.ts)

Lazy singleton — injects the YouTube script once per page, resolves when YT is ready:

loadYouTubeAPI().then(() => {
  playerRef.current = new window.YT.Player(containerRef.current, {
    videoId,
    playerVars: { autoplay: 1, mute: 1, controls: 1, loop: 1 },
    events: { onReady: (e) => { playerRef.current = e.target; } },
  });
});

This gives direct JS access to the player instance, so future controls like player.setVolume(n) or player.seekTo(t) require no structural changes — just a new prop and a call into playerRef.current.

Extending with a new player

To add e.g. a Vimeo player:

  1. Add 'vimeo' to VideoType in types.ts
  2. Create VimeoPlayer: React.FC<VideoPlayerProps>
  3. Register it: vimeo: VimeoPlayer in players.ts
  4. Add the detect rule in VideoPlayerWidget's EMBED_TO_VIDEO_TYPE map

No other files need to change.

C# API

Unchanged. Source, Autoplay, Controls, Muted, Loop, and Poster all serialize exactly as before.

Closes #2420

@rorychatt
Copy link
Copy Markdown
Collaborator

@claude review this.

Explain me, does this add value when we already have support for Embed where we can plug in youtube URL-s.
Can we trust the added package?

Maybe the implementation with the shared controls needs to be done to the Embed widget instead for Youtube client and others?

@ivy-interactive-claude-code

This comment was marked as off-topic.

@rorychatt
Copy link
Copy Markdown
Collaborator

@nwizugbesamson My main worry is that video player was meant to display videos hosted by the user, not for embedding (we have separate Embed)

I will give this another thought on Monday and discuss with the Ivy team

@rorychatt rorychatt self-requested a review March 15, 2026 10:35
@rorychatt rorychatt closed this Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(video) proposed interface and abstraction tsx

2 participants