{"id":13908003,"url":"https://github.com/lewhunt/react-tv-player","last_synced_at":"2025-04-12T22:31:44.319Z","repository":{"id":193556450,"uuid":"688997852","full_name":"lewhunt/react-tv-player","owner":"lewhunt","description":"A React video player for TV devices, with customisable buttons and arrow-key navigation. The component can play a variety of URLs like file paths, HLS/Dash streams and even YouTube links directly. Try out the demo on a desktop browser to see how it can simplify playback in your big-screen web apps: https://lewhunt.github.io/react-tv-player","archived":false,"fork":false,"pushed_at":"2024-11-28T15:20:40.000Z","size":1256,"stargazers_count":121,"open_issues_count":2,"forks_count":19,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-04T06:36:54.343Z","etag":null,"topics":["big-screen","bigscreen","dash","firetv","html5-video","iptv","live-streaming","media-player","ott","player","react","reactjs","smart-tv","spatial-navigation","tv","typescript","video","video-player","vimeo","youtube"],"latest_commit_sha":null,"homepage":"https://lewhunt.github.io/react-tv-player/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lewhunt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-08T14:54:03.000Z","updated_at":"2025-03-28T23:30:44.000Z","dependencies_parsed_at":"2024-04-26T11:45:42.060Z","dependency_job_id":"8b0beae3-da59-4bf5-83a7-78619ff0f4d3","html_url":"https://github.com/lewhunt/react-tv-player","commit_stats":{"total_commits":67,"total_committers":4,"mean_commits":16.75,"dds":"0.23880597014925375","last_synced_commit":"0064e2fe00355d2d36348fa8956ee401773df1ae"},"previous_names":["lewhunt/react-tv-player"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewhunt%2Freact-tv-player","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewhunt%2Freact-tv-player/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewhunt%2Freact-tv-player/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lewhunt%2Freact-tv-player/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lewhunt","download_url":"https://codeload.github.com/lewhunt/react-tv-player/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248640349,"owners_count":21138021,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["big-screen","bigscreen","dash","firetv","html5-video","iptv","live-streaming","media-player","ott","player","react","reactjs","smart-tv","spatial-navigation","tv","typescript","video","video-player","vimeo","youtube"],"created_at":"2024-08-06T23:02:23.633Z","updated_at":"2025-04-12T22:31:44.294Z","avatar_url":"https://github.com/lewhunt.png","language":"TypeScript","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"\u003ch1 align='center'\u003e\n  React TV Player\n\u003c/h1\u003e\n\n\u003cp align='center'\u003e\n  \u003ca href='https://www.npmjs.com/package/react-tv-player'\u003e\n    \u003cimg src='https://img.shields.io/npm/v/react-tv-player.svg' alt='Latest npm version'\u003e\n  \u003c/a\u003e\n    \u003ca href='https://github.com/lewhunt/react-tv-player/blob/main/LICENSE'\u003e\n    \u003cimg src='https://img.shields.io/badge/License-MIT-yellow.svg' alt='MIT License'\u003e\n  \u003c/a\u003e\n    \u003ca href='https://www.npmjs.com/package/react-tv-player'\u003e\n    \u003cimg src='https://img.shields.io/npm/dm/react-tv-player.svg' alt='Monthly npm downloads'\u003e\n  \u003c/a\u003e\n\n\u003c/p\u003e\n\n\u003cp align='center'\u003e\nA React media player component for TV devices. With custom UI and arrow-key navigation, it can play a variety of URLs like file paths, HLS/DASH streams and even YouTube links directly. Packaged as an npm library.\u003c/p\u003e\n\n[![https://lewhunt.github.io/react-tv-player/](https://repository-images.githubusercontent.com/688997852/cc39ebd0-f663-4715-b502-eccb06cc4e57)](https://lewhunt.github.io/react-tv-player/)\n\n\u003cp align='center'\u003e\u003ci\u003eClick on the image to try out the demo on a desktop browser\u003c/i\u003e\n\n## Installation\n\n```bash\nnpm install react-tv-player\n```\n\n## Usage\n\nJust import the player and render it in your app or page with [props](#props):\n\n```jsx\nimport { TVPlayer } from \"react-tv-player\";\n\nfunction App() {\n  return (\n    \u003c\u003e\n      \u003cTVPlayer url=\"https://www.youtube.com/watch?v=SkVqJ1SGeL0\" /\u003e\n    \u003c/\u003e\n  );\n}\n```\n\n### [👉 Try out the Demo](https://lewhunt.github.io/react-tv-player/) (on TV and desktop browsers)\n\nThe demo source code \u003ca href='https://github.com/lewhunt/react-tv-player/blob/main/src/App.tsx'\u003eApp.tsx\u003c/a\u003e illustrates how the component can be initialised with more [props](#props) such as metadata, [custom buttons](#custom-buttons), preview images and multiple media, enabling the user to cycle through videos with next/previous buttons:\n\n```jsx\n\u003cTVPlayer\n  title={mediaList[mediaIndex].title}\n  subTitle={mediaList[mediaIndex].subTitle}\n  url={mediaList[mediaIndex].url}\n  light={mediaList[mediaIndex].preview}\n  customButtons={customButtons}\n  mediaCount={mediaList.length}\n  onLikePress={handleLike}\n/\u003e\n```\n\n\u003cp\u003eHere is a short video of the \u003ca title=\"view demo\" href=\"https://lewhunt.github.io/react-tv-player/\"\u003edemo\u003c/a\u003e runnning on a browser:\u003c/p\u003e\n\nhttps://github.com/lewhunt/react-tv-player/assets/9886284/7baa4b75-491b-49f3-8cf1-698ae7f55941\n\n## Why Another Player?\n\nI've dedicated years working with various OTT players on big-screen devices. During this journey, two persistent challenges surfaced time and again: performant UI and compatible media encodings. These hurdles often forced us to heavily customise libraries and tackle streaming difficulties, leading to added costs and frustrating delays. 😫\n\nEnter ReactTVPlayer, an open-source media player component for TV devices that lowers the barrier to entry and seamlessly integrates with your React applications. :muscle:\n\n## What Sets It Apart?\n\nIt's designed for TV experiences out of the box, complete with customisable UI buttons and intuitive cursor plus arrow key navigation. 🎮 In addition to handling HLS and Dash streams effortlessly, it tackles the formidable challenge of playing YouTube and Vimeo urls directly on TV, \u003cb\u003esaving you the hassle and cost of additional video encoding.\u003c/b\u003e 🎉\n\n## How Does It Work?\n\nUnder the hood, this component harnesses the power of open-source libraries like Norigin Media's \u003ca href=\"https://github.com/NoriginMedia/Norigin-Spatial-Navigation\"\u003espatial navigation\u003c/a\u003e hook. It builds upon the excellence of \u003ca href=\"https://github.com/cookpete/react-player\"\u003eReact Player\u003c/a\u003e, which utilises \u003ca href=\"https://github.com/video-dev/hls.js\"\u003ehls.js\u003c/a\u003e and \u003ca href=\"https://github.com/Dash-Industry-Forum/dash.js\"\u003edash.js\u003c/a\u003e. Powered by React TypeScript (although you don't need to use TypeScript to make the most of it), this library is packaged efficiently using \u003ca href=\"https://vitejs.dev/\"\u003eVite\u003c/a\u003e, making integration a breeze. 🙌\n\n## Key Features\n\n- \u003cb\u003eVersatility\u003c/b\u003e: Customisable UI buttons, title metadata and preview images to suit your needs. It can effortlessly handle a variety of URLs, from mp4 file paths and HLS/DASH streams to services like YouTube and Vimeo.\n- \u003cb\u003eIntuitive Navigation\u003c/b\u003e: The player has been designed with TV experiences in mind. Cursor and arrow-key navigation make the user experience smooth and intuitive across big-screen platforms.\n- \u003cb\u003eYouTube Integration\u003c/b\u003e: One of its unique strengths is its ability to play URLs directly from the likes of YouTube, Vimeo, SoundCloud and Twitch, eliminating the need for additional video encoding when it's not needed.\n- \u003cb\u003eDRM Considerations\u003c/b\u003e: While it supports HLS AES Encryption, it’s built with flexibility in mind, allowing for future DRM integration with hls.js and dash.js.\n- \u003cb\u003eBroad Device Support\u003c/b\u003e: From Amazon Fire TV, LG webOS and Samsung Tizen Smart TVs to Xbox consoles and desktop web browsers, ReactTVPlayer covers most big-screen devices running web apps with post-2017 Chromium browsers.\n\n\u003chr\u003e\n\n## Props\n\nThe full list of props are below. Media related values such as `playing`, `loop` and `muted` are also mapped to state which can be accessed via the [useTVPlayerStore hook](#usetvplayerstore-hook) instead of updating props.\n\n| Prop                    | Description                                                                                                                                                                                                                                                                                                                                      | Default      |\n| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------ |\n| `url`                   | The url of the media to play \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;This can be an embedded url from YouTube/SoundCloud, a file path or a HLS or Dash manifest stream                                                                                                                                                                                              |\n| `playing`               | Set to `true` or `false` to pause or play the media. \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Set to `true` to autoplay the media (muted may also be needed in some browsers)                                                                                                                                                                                        | `false`      |\n| `loop`                  | Set to `true` to loop the media                                                                                                                                                                                                                                                                                                                  | `false`      |\n| `controls`              | Set to `true` to display native HTML5 media controls instead of custom TV Player UI controls                                                                                                                                                                                                                                                     | `false`      |\n| `light`                 | Set to `true` or a url `string` to show a preview image, which then loads the full player on selecting play\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Pass in true to use the default preview image associated with an embeded media url (e.g. YouTube/SoundCloud urls)\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Pass in an image URL to override any default preview image                 | `false`      |\n| `volume`                | Set the volume of the player, between `0` and `1`                                                                                                                                                                                                                                                                                                | `null`       |\n| `muted`                 | Set to `true` to mute the player\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;may be required if you intend to autoplay media                                                                                                                                                                                                                                             | `false     ` |\n| `playbackRate`          | Set the playback rate of the player\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Only supported by YouTube, Wistia, and file paths                                                                                                                                                                                                                                        | `1`          |\n| `disableFullscreen`     | Set to true to disable the defaulted fullscreen width and height of the player. \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Width and height values below should be defined \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;See \u003ca href='https://github.com/lewhunt/react-tv-player/blob/main/src/App.tsx'\u003eApp.tsx\u003c/a\u003e useEffect for example of toggling body properties based on fullscreen state | `false`      |\n| `disableInitNav`        | Set to true to disable initialisation of the norigin spatial navigation library. \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Set this if you are already initialising the navigation library in your own app                                                                                                                                                            | `false`      |\n| `width`                 | Set the width of the player when fullscreen is disabled                                                                                                                                                                                                                                                                                          | `100%`       |\n| `height`                | Set the height of the player when fullscreen is disabled                                                                                                                                                                                                                                                                                         | `350px`      |\n| `style`                 | Add [inline styles](https://facebook.github.io/react/tips/inline-styles.html) to the root element                                                                                                                                                                                                                                                | `{}`         |\n| `customButtons`         | Specify a collection of [custom buttons](#custom-buttons) for the player UI \u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;A set of \u003ca href=\"https://github.com/lewhunt/react-tv-player/blob/8b5bb4491b0407af518be96261398e534b50e8dd/src/lib/TVPlayerUI/TVPlayerUI.tsx#L83-L90\"\u003edefault buttons\u003c/a\u003e will be used otherwise.                                               | `null`       |\n| `title`                 | Set a `string` title for the current media.\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Embedded media urls such as YouTube will attempt to pull in the default media title if not overridden here.                                                                                                                                                                      |              |\n| `subTitle`              | Set a `string` sub-title for the current media.\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Embedded media urls such as YouTube will attempt to pull in the default author name if not overridden here.                                                                                                                                                                  |              |\n| `mediaCount`            | Set the total `number` of media items if you have multiple media and want player to display next and previous buttons                                                                                                                                                                                                                            | `0`          |\n| `mediaIndex`            | Set the initial media index `number` if you have multiple media and want player to handle next and previous buttons                                                                                                                                                                                                                              | `0`          |\n| `hideControlsOnArrowUp` | Set to `true` to hide the player UI by using the up arrow key when focussed on the player buttons                                                                                                                                                                                                                                                | `false`      |\n\n\u003chr\u003e\n\n## Callback Props\n\nCallback props take a function that gets fired on various player events and UI button actions:\n\n| Prop                 | Description                                                                                               |\n| -------------------- | --------------------------------------------------------------------------------------------------------- |\n| `onReady`            | Called when media is loaded and ready to play. If `playing` is set to `true`, media will play immediately |\n| `onStart`            | Called when media starts playing                                                                          |\n| `onPlay`             | Called when media starts or resumes playing after pausing or buffering                                    |\n| `onPause`            | Called when media is paused                                                                               |\n| `onBuffer`           | Called when media starts buffering                                                                        |\n| `onEnded`            | Called when media finishes playing\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Does not fire when `loop` is set to `true`         |\n| `onError`            | Called when an error occurs whilst attempting to play media                                               |\n| `onSkipBackPress`    | Called when the Skip Back button is pressed                                                               |\n| `onSkipForwardPress` | Called when the Skip Forward button is pressed                                                            |\n| `onPreviousPress`    | Called when the Previous button is pressed                                                                |\n| `onNextPress`        | Called when the Next button is pressed                                                                    |\n| `onLikePress`        | Called when the Like button is pressed                                                                    |\n| `onLoopPress`        | Called when the Loop button is pressed                                                                    |\n| `onMutePress`        | Called when the Mute button is pressed                                                                    |\n| `onFullscreenPress`  | Called when the Fullscreen button is pressed                                                              |\n\n\u003chr\u003e\n\n## Custom Buttons\n\nAs 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.\n\n```jsx\nimport { TVPlayer, TVPlayerButtonProps } from \"react-tv-player\";\nimport { faGithub } from \"@fortawesome/free-brands-svg-icons\";\n\nconst customButtons: TVPlayerButtonProps[] = [\n  { action: \"loop\", align: \"left\" },\n  { action: \"like\", align: \"left\" },\n  { action: \"previous\", align: \"center\" },\n  { action: \"playpause\", align: \"center\" },\n  { action: \"next\", align: \"center\" },\n  { action: \"mute\", align: \"right\" },\n  {\n    action: \"custom\",\n    align: \"right\",\n    label: \"About\",\n    faIcon: faGithub,\n    onPress: () =\u003e {\n      window.location.href = \"https://github.com/lewhunt/react-tv-player\";\n    },\n  },\n];\n\n\u003cTVPlayer\n  url=\"https://www.youtube.com/watch?v=SkVqJ1SGeL0\"\n  customButtons={customButtons}\n/\u003e;\n```\n\n| Button Props     | Description                                                                                                                                        |\n| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `action`         | Choose from `custom` or one of the pre-built actions: `like`, `loop`, `mute`,`next`,`playpause`,`previous`,`skipforward`, `skipback`, `fullscreen` |\n| `align`          | Alignment of the button. Choose from `left`,`center`, `right`                                                                                      |\n| `label`          | A hint text label to appear below the current button in focus. Pre-built button actions use relevent labels.                                       |\n| `faIcon`         | A font-awesome icon. Pre-built button actions use relevent icons.                                                                                  |\n| `onPress`        | Called when a button is pressed. Pre-built button actions have their own behaviours.                                                               |\n| `onRelease`      | Called when a button is released. Currently unused.                                                                                                |\n| `isSelectedFill` | Allows support of toggle behaviour (in the form of a button fill) when set to true.                                                                |\n| `disable`        | Prevents button action when set to true.                                                                                                           |\n\n\u003chr\u003e\n\n## useTVPlayerStore Hook\n\nFor 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:\n\n```jsx\n// 1. import useTVPlayerStore\nimport { TVPlayer, useTVPlayerStore } from \"react-tv-player\";\n\n// 2. get state values (there are more availble, see TVPlayerUI.ts for reference)\nconst actions = useTVPlayerStore((s) =\u003e s.actions);\nconst playing = useTVPlayerStore((s) =\u003e s.playing);\nconst player = useTVPlayerStore((s) =\u003e s.player);\nconst likeToggle = useTVPlayerStore((s) =\u003e s.likeToggle);\ns;\n\nconst logPlaybackState = () =\u003e console.log(playing);\n\n//3. set state using the actions object\nconst handleLike = () =\u003e {\n  console.log(\"like button pressed\");\n  actions.setLikeToggle(!likeToggle);\n};\n\nconst togglePlayback = () =\u003e {\n  actions.setPlaying(!playing);\n};\n\n//4. access player instance methods via the player state\nconst customSeek = () =\u003e player.seekTo(player.getCurrentTime() + 10);\n\n\u003cTVPlayer\n  url=\"https://www.youtube.com/watch?v=SkVqJ1SGeL0\"\n  onLikePress={handleLike}\n/\u003e;\n```\n\n\u003chr\u003e\n\n## Instance Methods\n\nUse the state's `player` reference - as in the above example - to call instance methods on the player.\n\n| Method                 | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |\n| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `seekTo(amount, type)` | Seek to the given number of seconds, or fraction if `amount` is between `0` and `1`\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;`type` parameter lets you specify `'seconds'` or `'fraction'` to override default behaviour                                                                                                                                                                                                                                                                                                                                                                                                                                                  |\n| `getCurrentTime()`     | Returns the number of seconds that have been played\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Returns `null` if unavailable                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |\n| `getSecondsLoaded()`   | Returns the number of seconds that have been loaded\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Returns `null` if unavailable or unsupported                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| `getDuration()`        | Returns the duration (in seconds) of the currently playing media\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Returns `null` if duration is unavailable                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |\n| `getInternalPlayer()`  | Returns the internal player of whatever is currently playing\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;eg the [YouTube player instance](https://developers.google.com/youtube/iframe_api_reference#Loading_a_Video_Player), or the [`\u003cvideo\u003e`](https://developer.mozilla.org/en/docs/Web/HTML/Element/video) element when playing a video file\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Use `getInternalPlayer('hls')` to get the [hls.js](https://github.com/video-dev/hls.js) player\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Use `getInternalPlayer('dash')` to get the [dash.js](https://github.com/Dash-Industry-Forum/dash.js) player\u003cbr /\u003e\u0026nbsp; ◦ \u0026nbsp;Returns `null` if the internal player is unavailable |\n\n\u003chr /\u003e\n\n## Support\n\nI hope this has given a good intro to the component.\n\n[💬 Fire over a comment](https://github.com/lewhunt/react-tv-player/issues) if you have any feedback, requests or issues 🐛\n\n[⭐ Give it a star](https://github.com/lewhunt/react-tv-player) if you like the component or want to bookmark it 🙏\n\n\u003cbr/\u003e\n\n### [👉 Try out the Demo](https://lewhunt.github.io/react-tv-player/) (on TV and desktop browsers)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flewhunt%2Freact-tv-player","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flewhunt%2Freact-tv-player","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flewhunt%2Freact-tv-player/lists"}