0.0.1-test • Published 2 years ago

ps-hackathon-video-player v0.0.1-test

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

Video Player

Install

yarn add @a-cloud-guru/video-player

Usage

There's a few examples of usage:

  • examples/create-react-app
  • src/__stories__/Player.stories.js

The player is made of two components: PlayerProvider and Player, the idea being to wrap the Player in a PlayerProvider:

const Component = () => (
  <PlayerProvider sources={sources}>
    <Player />
  </PlayerProvider>
)

where sources looks like:

const sources = {
  '360p': "http://source",
  '720p': "http://source",
  // whatever you want here
  'quality': "src",
}

We also expose a usePlayer hook, allowing a component to access the player state and a few actions:

// player-info.js
export const PlayerInfo = () => {
  const [{ playing }, { play, pause }] = usePlayer();
  return (
    <div>
      <p>Player is {playing ? 'playing' : 'paused'}.</p>
      <button onClick={playing ? pause : play}>{playing ? 'Pause' : 'Play'}</button>
    </div>
  )
}

// another-component.js
import { PlayerInfo } from './player-info';

<PlayerProvider sources={sources}>
  <Player />
  <PlayerInfo />
</PlayerProvider>

List of properties accessible through usePlayer

const [state, actions] = usePlayer();

--STATE-- | property | default value | description | | --- | --- | --- | | pip | false | is picture in picture enabled? | muted | false | is the audio muted? | controlBarVisible | false | is the control bar visible? | settingsVisible | false | are the settings visibile? | autoplay | true | is autoplay on? | volume | 1 | current volume value | playing | false | is the video playing? | loading | true | is the video still loading? | seeking | false | are we currently seeking forward/backward? | durationSeconds | 0 | total length of the video | playedSeconds | 0 | current position in the video | interval | { back:15, forward:15, } | number of seconds to seek backward/forward | subtitleEnabled | false | are subtitles enabled? | quality | '720p' | current quality | playbackRate | 1 | current playback rate | completedTimerCallbacks | [] | list of timer callbacks we've already called; avoids triggering them twice | fullscreen | false | is the player fullscreen? | sources | undefined | array of sources; refer to the next section for details | subtitle | undefined | subtitle url; refer to the next section for details | currentCueText | '' | current subtitle text | url | sourcesquality || sources.Auto | current url | compact | false | is the player in "compact" mode? more on that in the next section | hidden | false | is the player "hidden"? more on that in the next section

--ACTIONS-- | property | type | description | | --- | --- | --- | | reset | function | reset the video player to its initial state | | play | function | play the video | | pause | function | pause the video | | togglePlay | function | play/pause helper | | prev | function | trigger the "onPrevious" callback | | next | function | trigger the "onNext" callback | | playAgain | function | seek back to the beginning of the video | | mute | function | mute the audio | | unmute | function | unmute the audio | | toggleMute | function | mute/unmute helper | | updateVolume | function | update the volume to a new value | | seekTo | function | move to a specific second in the video | | forward | function | seek forward state.interval.forward seconds | | backward | function | seek backward state.interval.backward seconds | | fullScreen | function | enables fullscreen | | exitFullScreen | function | disables fullscreen | | updatePlaybackRate | function | change the playback rate | | updateSeekInterval | function | call it with an object containing {forward:seconds} or {backward:seconds} to update the seek interval | | updateQuality | function | update the quality of the video; effectively switches the source | | toggleAutoplay | function | enable/disable autoplay | | togglePiP | function | enable/disable picture in picture | | canEnablePiP | function | is PiP supported by the browser? call it with the url for the current source | | toggleSubtitle | function | turn subtitles on/off | | canEnableSubtitle | boolean | do we have subtitles? | | player | React ref | react reference to the video player element |

List of properties on the PlayerProvider

propertytypedescription
sourcesobjectas described above, this contains a list of quality/source pairs. This is the only actually required field. const sources = { '360p': "http://source", '720p': "http://source", 'quality': "src", }
subtitlestringan url to a subtitles file.
getPlayerContainerfunctionused to get a reference to the container parent. Used to handle onClick events for example. Example: () => document.body.
hiddenbooleanbecause mounting the player on the page starts loading the video, it's sometimes useful to put the player on the page before it's actually visibile. Using this parameter, we can mount the component, but hide the player and disable a few things which would be disruptive, like the keyboard bindings for example.
compactbooleanused to display a smaller version of the video player – currently unstable
showFullControlsbooleanwhether to display "next" and "previous" buttons in the control bar
onPreviousfunctiontriggered when prev() is called.
onErrorfunctiontriggered when there's an error trying to play the video, as defined by react-player here.
onNextfunctiontriggered when next() is called.
keyPropstringa value that is used as a key on the underlying component. Useful to re-render the whole player when changing videos for example. Example: ${courseId}-${lessonId}.
onUpdateProgressfunctioncalled by onProgress, as defined by react-player here
onControlBarobjectcontains a list of callbacks (functions), triggered in various occasions. More on that later.
timerCallbacksarraya list of callbacks to be called at very specific time points in the video. It's been designed to be as flexible as possible, more on that later.
trackFnRefReact refa react reference to a function; essentially a reference where current is a function, called every time the player wants to track a "watched" event.
maxTimeOfNoActivitynumber, millisecondsDefaults to 5000 (5 seconds). When a user pauses the video, this is the delay after which we send the event through the track function.
maxTimeThresholdnumber, millisecondsDefaults to 270000 (270 seconds, or 4m30s). While the video is still playing, this is the delay after which we send an event through the track function

--ON CONTROL BAR--

These callbacks are available in the property mentioned in the previous section, called onControlBar. | property | type | description | | --- | --- | --- | | onPlay | function | called before the playback is resumed, using play() | onPause | function | called before the playback is paused, using pause() | onAutoPlayChange | function | called before toggling the autoplay, using toggleAutoplay(). The value is a boolean representing the active state. | onPiPChange | function | called before toggling picture in picture, using togglePip(). The value is a boolean representing the active state. | onPlaybackRateChange | function | called before changing the speed of the playback, using updatePlaybackRate(). The value is the new playback rate. | onSeekForward | function | called before seeking forward in the video, using forward(). The value is an object of the following shape { from: where_we_were_in_seconds, to: where_we_moved_in_seconds } | onSeekBackward | function | called before seeking backward in the video, using backward(). The value is an object of the following shape { from: where_we_were_in_seconds, to: where_we_moved_in_seconds } | onToggleSubtitle | function | called before toggling the subtitles, using toggleSubtitle(). The value is a boolean representing the active state. | onSendFeedback | function | called when the "send feedback" button is clicked

--ABOUT TIMER CALLBACKS--

const timerCallbacks = [
  {
    type: "start",
    second: 23,
    callback: () => console.log("called at 23s from the start")
  },
  {
    type: "start",
    percent: 0.3,
    callback: () => console.log("called at 30% of the video")
  },
  {
    type: "end",
    second: 10,
    callback: () => console.log("called at 10s before the end")
  },
  {
    type: "end",
    percent: 20,
    callback: () => console.log("called at 20% from the end / 80% of the video")
  }
]

--VIDEO TRACKING--

  • when a user starts watching a video, every 270s (4min 30s), we send an event – you can think of it as some sort of heartbeat.
  • if the user pauses the video, we send the event after 5 seconds, to avoid being spammed by double clicks on the play button).
  • if a user is watching a video, doesn’t reach the 270s required to trigger the event, but closes their browser/computer/tab/etc, we just keep a record in their browser’s local storage – when they come back to our website, the event we stored here will get sent, so that we don’t lose the time they spent watching in their previous session.

--TRACKING EVENTS--

propertytypedescription
actualTimenumber, in millisecondsthis is the actual time, as in the time that the user spends in front of the video.
contentTimenumber, in secondsthis is the content time counterpart – the duration of video content the user has watched. Note that actualTime and contentTime can be differ because of the playback speed.
fromnumber, in secondswhen did we start tracking for the current event?
tonumber, in secondswhen did we stop tracking for the current event?
trackedLaterbooleanwhether it’s an event that we could not track because the user moved away from the app, so we tracked it when they came back.
originalTrackDatetimestampthis is a timestamp, corresponding to when we recorded the event. If the event is tracked right away, this will be very close to when we actually register the event in Segment, but when it’s tracked after the user has left the app and come back (when trackedLater = true), this originalTrackDate will hold the date at which we recorded the event
currentPlaybackRatenumberthe speed at which the video is played
currentSubtitleEnabledbooleanwhether the subtitles are on or off
currentPipbooleantrue if the user is watching the video in Picture-in-Picture
currentQualitystringthe quality the user is watching the video at

List of properties on the Player component

propertytypedescription
containerStyleobjectspread in the sx prop associated with the player container component (more about it here, it's essentially react styles)
controlBarStyleobjectspread in the sx prop associated with the control bar component
onEndedCallbackfunctiona hook allowing the caller to precisely control what to do at the end of the video.

--ON ENDED CALLBACK--

We've used this to show a popup at the end of the video, only if a set of conditions is fulfilled. Implementation details and example:

const onEndedCallback = ({defaultOnEnd}) => {
  if (Math.random() > 0.5) {
    // do something here, show a popup, etc
    // ...
  } else {
    defaultOnEnd();
  }
}

// implementation details inside Player.js
<ReactPlayer
// onEnded triggered by react-player, as described here: https://github.com/CookPete/react-player#callback-props
  onEnded={() => {
    const defaultOnEnd = () => autoplay && next() && onAutoplay();
    // if onEndedCallback is provided, call it with defaultOnEnd as a param
    return onEndedCallback
      ? onEndedCallback({ pip, fullscreen, defaultOnEnd, durationSeconds })
    // otherwise just call defaultOnEnd
      : defaultOnEnd();
  }}
/>

TODO

  • consider decoupling tracking; should it be in the base player or in a wrapper?
  • fix the disabled linting rules