Customizable React Video Player for TV Devices with Arrow Key Navigation

React TV Player

A React video player component for TV devices, with customisable buttons and arrow key navigation. It can play a variety of URLs including file paths, YouTube, HLS and Dash streams.

https://lewhunt.github.io/react-tv-player

Click on the image to try out the demo on a desktop browser

Usage

npm install react-tv-player
import React from "react";
import { TVPlayer } from "react-tv-player";

// Render a YouTube video player for TV
<TVPlayer url="https://www.youtube.com/watch?v=SkVqJ1SGeL0" />;

Background

In the dynamic landscape of the TV industry, I’ve dedicated years to working with various video players. During this journey, two persistent challenges surfaced time and again: performant UI navigation and compatible streaming protocols. These hurdles often forced us to heavily customise players and tackle media encoding difficulties, leading to added costs and frustrating delays. 😫

Why React TV Player?

Enter React TV Player, an innovative open-source component that seamlessly integrates with your React applications. It brings forth a media player tailored for TV experiences, complete with intuitive arrow key and cursor navigation. 📺 🎮

What Sets It Apart?

But that’s not all. React TV Player isn’t just another player. It’s a versatile solution that handles HLS and Dash streams effortlessly. What’s more, it tackles the formidable challenge of playing YouTube videos – yes YouTube – eliminating the need for custom video encoding when it’s not necessary. 🎉

How Does It Work?

Under the hood, this component harnesses the power of open-source libraries like Norigin Media’s spatial navigation hook. It builds upon the excellence of React Player, which utilises hls.js and dash.js. Powered by React TypeScript (although you don’t need to use TypeScript to make the most of it), this library is packaged efficiently using Vite, making integration a breeze. 🙌

Demo

The demo source code App.tsx illustrates how the component can be initialised with metadata, custom buttons, preview images and multiple media, enabling the user to cycle through videos with next/previous buttons and handle actions such as the Like button.

<TVPlayer
  title={mediaList[mediaIndex].title}
  subTitle={mediaList[mediaIndex].subTitle}
  url={mediaList[mediaIndex].url}
  light={mediaList[mediaIndex].preview}
  customButtons={customButtons}
  mediaCount={mediaList.length}
  onLikePress={handleLike}
/>

Here is a short video of the demo runnning on a browser:

react-tv-player-demo-480p.mov


Props

The full list of props are listed below. Media related values such as playingloop and muted are also mapped to state which can be accessed via the useTVPlayerStore hook instead of updating props.

PropDescriptionDefaulturlThe url of the media to play ◦ This can be an embedded url from YouTube/SoundCloud, a file path or a HLS or Dash manifest streamplayingSet to true or false to pause or play the media. ◦ Set to true to autoplay the media (muted may also be needed in some browsers)falseloopSet to true to loop the mediafalsecontrolsSet to true to display native HTML5 media controls instead of custom TV Player UI controlsfalselightSet to true or a url string to show a preview image, which then loads the full player on selecting play ◦ Pass in true to use the default preview image associated with an embeded media url (e.g. YouTube/SoundCloud urls) ◦ Pass in an image URL to override any default preview imagefalsevolumeSet the volume of the player, between 0 and 1nullmutedSet to true to mute the player ◦ may be required if you intend to autoplay mediafalseplaybackRateSet the playback rate of the player ◦ Only supported by YouTube, Wistia, and file paths1widthSet the width of the player100%heightSet the height of the player100%styleAdd inline styles to the root element{}customButtonsSpecify a collection of custom buttons for the player UI ◦ A set of default buttons will be used otherwise.nulltitleSet a string title for the current media. ◦ Embedded media urls such as YouTube will attempt to pull in the default media title if not overridden here.subTitleSet a string sub-title for the current media. ◦ Embedded media urls such as YouTube will attempt to pull in the default author name if not overridden here.mediaCountSet the total number of media items if you have multiple media and want player to display next and previous buttons0mediaIndexSet the initial media index number if you have multiple media and want player to handle next and previous buttons0

Callback props

Callback props take a function that gets fired on various player events and UI button actions:

PropDescriptiononReadyCalled when media is loaded and ready to play. If playing is set to true, media will play immediatelyonStartCalled when media starts playingonPlayCalled when media starts or resumes playing after pausing or bufferingonPauseCalled when media is pausedonBufferCalled when media starts bufferingonEndedCalled when media finishes playing ◦ Does not fire when loop is set to trueonErrorCalled when an error occurs whilst attempting to play mediaonSkipBackPressCalled when the Skip Back button is pressedonSkipForwardPressCalled when the Skip Forward button is pressedonPreviousPressCalled when the Previous button is pressedonNextPressCalled when the Next button is pressedonLikePressCalled when the Like button is pressedonLoopPressCalled when the Loop button is pressedonMutePressCalled when the Mute button is pressed

Custom Buttons

As illustrated in the sample demo app, the player can be overridden with custom buttons. There is a selection of pre-built action types with their own icons and behaviours or you can add your own with the “custom” action type.

import { TVPlayer, TVPlayerButtonProps } from "react-tv-player";
import { faGithub } from "@fortawesome/free-brands-svg-icons";

const customButtons: TVPlayerButtonProps[] = [
  { action: "loop", align: "left" },
  { action: "like", align: "left" },
  { action: "previous", align: "center" },
  { action: "playpause", align: "center" },
  { action: "next", align: "center" },
  { action: "mute", align: "right" },
  {
    action: "custom",
    align: "right",
    label: "About",
    faIcon: faGithub,
    onPress: () => {
      window.location.href = "https://github.com/lewhunt/react-tv-player";
    },
  },
];

<TVPlayer
  url="https://www.youtube.com/watch?v=SkVqJ1SGeL0"
  customButtons={customButtons}
/>;

Button PropsDescriptionactionChoose from custom or one of the pre-built actions: likeloopmute,next,playpause,previous,skipforwardskipbackalignAlignment of the button. Choose from left,centerrightlabelA hint text label to appear below the current button in focus. Pre-built button actions use relevent labels.faIconA font-awesome icon. Pre-built button actions use relevent icons.onPressCalled when a button is pressed. Pre-built button actions have their own behaviours.onReleaseCalled when a button is released. Currently unused.isSelectedFillAllows support of toggle behaviour (in the form of a button fill) when set to true.disablePrevents button action when set to true.

useTVPlayerStore hook

For more control you can import the useTVPlayerStore custom hook to globally access player state (zustand store). View the sample app and the TVPlayerUI inner component for examples of use. Below shows the basics:

// 1. import useTVPlayerStore
import { TVPlayer, useTVPlayerStore } from "react-tv-player";

// 2. get state values (there are more availble, see TVPlayerUI.ts for reference)
const actions = useTVPlayerStore((s) => s.actions);
const playing = useTVPlayerStore((s) => s.playing);
const player = useTVPlayerStore((s) => s.player);
const likeToggle = useTVPlayerStore((s) => s.likeToggle);
s;

const logPlaybackState = () => console.log(playing);

//3. set state using the actions object
const handleLike = () => {
  console.log("like button pressed");
  actions.setLikeToggle(!likeToggle);
};

const togglePlayback = () => {
  actions.setPlaying(!playing);
};

//4. access player instance methods via the player state
const customSeek = () => player.seekTo(player.getCurrentTime() + 10);

<TVPlayer
  url="https://www.youtube.com/watch?v=SkVqJ1SGeL0"
  onLikePress={handleLike}
/>;

Instance Methods

Use the state’s player reference – as in the above example – to call instance methods on the player.

MethodDescriptionseekTo(amount, type)Seek to the given number of seconds, or fraction if amount is between 0 and 1 ◦ type parameter lets you specify 'seconds' or 'fraction' to override default behaviourgetCurrentTime()Returns the number of seconds that have been played ◦ Returns null if unavailablegetSecondsLoaded()Returns the number of seconds that have been loaded ◦ Returns null if unavailable or unsupportedgetDuration()Returns the duration (in seconds) of the currently playing media ◦ Returns null if duration is unavailablegetInternalPlayer()Returns the internal player of whatever is currently playing ◦ eg the YouTube player instance, or the <video> element when playing a video file ◦ Use getInternalPlayer('hls') to get the hls.js player ◦ Use getInternalPlayer('dash') to get the dash.js player ◦ Returns null if the internal player is unavailable

DRM Support

You can use HLS AES Encryption, but it currently does not support Widevine, FairPlay or PlayReady DRM out of the box. However, when playing files it renders a video tag which can be accessed with the getInternalPlayer() instance method, mentioned above. So there is scope to hook into hls.js and dash.js for further DRM integration if desired. More work on this is on the future roadmap.

Device Support

The library has been put through some initial testing on desktop web browsers and TV web app platforms such as Amazon FireTV, Samsung Tizen, Xbox UWP and LG webOS. More checks will be carried out over the next few months. Generally TV devices post-2018 will be better supported as they’ll have more modern Chromium browsers.

Due to various restrictions, React TV Player is not intended to work properly on smaller mobile devices. The UI is designed for widescreen displays and YouTube player documentation explains that certain mobile browsers require user interaction before playing.

You can use a desktop browser with arrow-keys to simulate the TV experience.

GitHub

View Github

Related Posts

Recent Posts

ഇടുക്കിയിലെ മലയോര മേഖലകളിൽ രാത്രിയാത്ര നിരോധിച്ചു. രാത്രി ഏഴു മുതൽ രാവിലെ ആറു വരെയാണ് നിരോധനം

ഏന്തയാർ ഈസ്റ്റിൽ പ്രളയത്തിൽ തകർന്ന പാലത്തിന് പകരം പുതിയ പാലം നിർമ്മിക്കുവാൻ താത്ക്കാലിക പാലം പൊളിച്ച് നീക്കി

Explore the Investment Opportunities: A Comprehensive Guide to Different Types of Mutual Funds

Title: Understanding Mutual Funds: A Beginner's Guide to Investing

തീവ്രമഴ മുന്നറിയിപ്പിന്റെ പശ്ചാതലത്തിൽ സംസ്ഥാനം ജാഗ്രതയിൽ

250,000 അപേക്ഷകൾ വർദ്ധിച്ചതിനാൽ ട്രാൻസ്‌പോർട്ട് കമ്മീഷണർ പരിശോധന പുനരാരംഭിക്കും

ഏലക്കയിൽ കീടനാശിനി സാന്നിധ്യം; ആറര ലക്ഷത്തിലധികം ടിൻ അരവണ നശിപ്പിക്കാൻ ടെൻഡർ ക്ഷണിച്ച് ദേവസ്വം ബോർഡ്‌

ഭീമൻ പാറക്കഷണങ്ങൾ അടർന്ന് ദേശീയ പാതയിലേക്ക് വീഴുന്നത് പതിവാകുന്നു. കുട്ടിക്കാനത്തിനും മുണ്ടക്കയത്തിനുമിടയിൽ നിലനിൽക്കുന്നത് വൻ അപകട ഭീഷണി

ചക്രവാതച്ചുഴി:അതിശക്തമായ മഴ വരുന്നു

പ്ലസ് വൺ പ്രവേശനം. അക്ഷയയിൽ തിക്കി തിരക്കേണ്ട, നെറ്റിവിറ്റി/ജാതി തെളിയിക്കാൻ പത്താംതരം സർട്ടിഫിക്കറ്റ് മതി