# VoiceBasePlayer

[![npm package][npm-badge]][npm]

[npm-badge]: https://img.shields.io/npm/v/npm-package.png?style=flat-square
[npm]: https://www.npmjs.org/package/npm-package

The VoiceBase React Player is a React component designed to 
display and play transcripts and media. 

Unlike previous editions the Voicebase React Player expects
to be fed a mediaUrl and an analytics (JSON) object. 

Alternately the `VoiceBasePlayer` has a `loading` and `error` screen that can be displayed
during retrieval of content. 

## API

The API is modal - only when loading and error are both false (opmitted) 
does the VoiceBase Player require a complete set of properties describing media. 

### When Ready To Play:

* `analytics` - JSON object: describing transcript, predefined vocabulary, etc.
* `analyticsFormat` - string (optional): one of the following constants: 
  `ANALYTICS_SCHEMA_VERSION_V2`, `ANALYTICS_SCHEMA_VERSION_V2_CALLBACK`,
   `ANALYTICS_SCHEMA_VERSION_V3` (the default value),
   `ANALYTICS_SCHEMA_VERSION_EVENT_STREAM`.
* `mediaUrl` - string: a fully qualified hyperlink to an audio file/stream.

*`loading` and `error` must be false or omitted to view the Player control panel. 

```jsx harmony
<VoiceBasePlayer analytics={myAnalytics} 
analyticsFormat={ANALYTICS_SCHEMA_VERSION_V2_CALLBACK}
mediaUrl={myMediaURL} />

```

The control panel will appear like this:

![Player screen](doc/img/player-playing.png)

The children of the `VoiceBasePlayer` tag are ignored during playback.

## While Loading

If you want to delay projection of the `VoiceBasePlayer` panel you can of course
simply not display it until you have the mediaUrl and analytics ready, 
showing any ajax visual display you please. 
However, if you pass `loading={true}` to VoiceBasePlayer it will display a loading 
spinner. Any content of the`VoiceBasePlayer` tag will be displayed.

```jsx harmony
<VoiceBasePlayer loading={true}>Getting Source Files, Please Wait
</VoiceBasePlayer>
```

The load screen appears as this:

![Player screen](doc/img/player-loading.png)

## On an error:

While you can display your own error screen on errors, the Player will serve 
as a basic error panel. The only parameter that must be set is:

* `error`: true

The error message (optional) can be passed through in the body of the
 `<VoieBasePlayer>{my message}</VoiceBasePlayer>` tag.
 
You can put any React tag you want as a child of `VoiceBasePlayer` react and it
will be shown inside a panel. 

```jsx harmony
<VoiceBasePlayer error={true}>Generic URL Error Message</VoiceBasePlayer>
```

The error screen appears like this:

![Player screen](doc/img/player-error.png)

# Retrieval of Media URLs and Analytics from VoiceBase Servers

Note - previous versoins of the VoiceBasePlayer "internalized" the function of accessing
content from VoiceBase Servers. the current player does not - it expects you to get those data
yourself. 

There are VoiceBase APIs that will provide this information. However they require a Bearer Token
to access.

There are two APIs - one to get the mediaUrl, and another to get the Analytics Data.

The first returns a string; the second returns the Analytics JSON as a body. 

There are functions that will allow you to more easily access that data. You will have to
integrate them into your container and coordinate those properties within a wrapping context.

Here is an example of how you would do this. 

In this usage, "getProps()" is a utility to pull attributes out of the page URL/search string. 
You could just as well pull these values from a form.

With this example, we transport the player's usable properties into React component state.

```jsx harmony

import React, { Component } from 'react';
import _ from 'lodash';
import ReactDOM from 'react-dom';

import VoiceBasePlayer, { getVoicebaseMediaUrl, getVoicebaseAnalytics } from '@voicebase/react-player';

import getProps from './getProps';

const centerText = {
  textAlign: 'center',
  fontSize: '1rem',
};

class PlayerWrapper extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  static getDerivedStateFromProps(props) {
    return props;
  }

  componentDidMount() {
    if (this.state.token && this.state.mediaId) {
      getVoicebaseMediaUrl(this.state.token, this.state.mediaId)
        .then(({ url }) => {
          this.setState({ mediaUrl: url });
        })
        .catch((mediaUrlLoadError) => {
          this.setState({ mediaUrlLoadError });
        });

      getVoicebaseAnalytics(this.state.token, this.state.mediaId, this.state.apiSuffix)
        .then((analytics) => {
          this.setState({ analytics });
        })
        .catch((analyticsLoadError) => {
          this.setState({ analyticsLoadError });
        });
    } else {
      console.log('no token/media id:', this.state);
    }
  }

  render() {
    if (this.state.mediaUrlLoadError || this.state.analyticsLoadError) {
      { /-------- we have errors; display those to the user. -------/ }
      return (
        <VoiceBasePlayer error={true}>
          {this.state.mediaUrlLoadError && (
            <p style={centerText}>
              {_.get(this, 'state.mediaUrlLoadError.message', 'Error loading Media')}
            </p>
          )}
          {this.state.analyticsLoadError && (
            <p style={centerText}>
              {_.get(this, 'state.analyticsLoadError.message', 'Error loading Analytics')}
            </p>
          )}
        </VoiceBasePlayer>
      );
    } else if (this.state.mediaUrl && this.state.analytics) {
      { /-------- we can play media now. Show the player control panel. -------/ }
      return <VoiceBasePlayer {...this.state} />;
    }
   
    { /-------- Without errors OR playing fields, display the loading spinner. -------/ }
    return (
      <VoiceBasePlayer loading={true}>
        <p style={centerText}>Getting source files, please wait</p>
      </VoiceBasePlayer>
    );
  }
}

const props = getProps();

ReactDOM.render(
  <PlayerWrapper {...props} />,
  document.getElementById('root'),
);

```

The `render()` branching is useful to pore over. It only displays the `VoiceBasePlayer`
in play mode if it has the required fields `analytics` and `mediaUrl`. `analyticsFormat`
(an optional field with a default) may or may not be present. 

`render()` falls through to the loading screen if no errors are present AND 
the required playing fields are also not complete.

# Passing External Hits from the Player URL

When the player is hosted (embedded) in another program, such as the portal, you can pass in hit information in JSON format as a URL parameter to display on the right side panel.

You can specify a Section Title as well as a group title. There is also a boolean field ("showSubItems") for controlling the display of the sub items.

Here is the format of the JSON data that you must URL encode and include as the content of a URL parameter named "externalHits":

```jsx harmony

{
  "SectionTitle": "Section Title",
  "groupTitle": "Greatest Hits",
  "showSubItems": false,
  "hits": [
    {
      "s": [5433,5999],
      "e": [5667,6099],
      "label": "Hit 1"
    },
    {
      "s": [6133],
      "e": [6667],
      "label": "Hit 2"
    },
    {
      "s": [7033],
      "e": [7067],
      "label": "Hit 3"
    }
  ]
}

```