diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..8efdb67
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,13 @@
+{
+ "editor.formatOnSaveMode": "file",
+ "editor.formatOnSave": true,
+ "editor.codeActionsOnSave": {
+ "source.organizeImports": "explicit"
+ },
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 401bde5..454b81e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,46 +1,65 @@
# Changelog
+
All changes to this project will be documented in this file.
+## [1.2.29] - 2024-06-19
+
+- Add custom domain
+
## [1.2.28] - 2023-12-11
+
- Add experimental methods
## [1.2.27] - 2023-12-08
+
- Add chromecast & airplay events
## [1.2.26] - 2023-10-05
+
- Add isLiveStream()
## [1.2.25] - 2023-06-02
+
- Add ads support
## [1.2.24] - 2023-03-03
+
- Add `sequence` option
-
+
## [1.2.23] - 2023-02-15
+
- Add `privateSession` option
## [1.2.22] - 2022-11-04
+
- Add `getPlaying()` method
## [1.2.21] - 2022-05-23
+
- Apply `setVideoStyleObjectFit()` and `setVideoStyleTransform()` to poster.
## [1.2.20] - 2022-05-23
+
- Add `setVideoStyleObjectFit()` and `setVideoStyleTransform()` methods.
-
+
## [1.2.19] - 2022-05-19
+
- When loadConfig() is called, apply the last config settings
-
+
## [1.2.18] - 2022-05-12
+
- Fix showControls & hideControls methods when called before the player is ready.
-
+
## [1.2.17] - 2022-04-27
+
- add exitFullscreen(), requestFullscreen(), exitPictureInPicture() and requestPictureInPicture()
## [1.2.16] - 2022-04-25
+
- add showPoster() and hidePoster()
## [1.2.15] - 2022-04-20
+
- Add event callback types
- Delay some methods execution after ready event is received
- Add playbackRate option
@@ -48,47 +67,61 @@ All changes to this project will be documented in this file.
- Add setAutoplay() method
## [1.2.14] - 2022-04-11
+
- Add `chromeless` mode
- Add `hidePoster` option
## [1.2.13] - 2022-04-01
+
- Add the possibility to show/hide the "more" button
## [1.2.12] - 2021-12-23
+
- Add the possibility to directly provide a DOM Element at instantiation
-
+
## [1.2.11] - 2021-12-15
+
- Add `controls` parameter in `hideControls()` and `showControls()`
## [1.2.10] - 2021-12-10
+
- Add `allow="autoplay"` in iframe tag
## [1.2.9] - 2021-10-07
+
- Add setTheme method
## [1.2.8] - 2021-05-28
+
- Add showSubtitles() / hideSubtitles() methods
-
+
## [1.2.7] - 2021-05-28
+
- Add "token" in SdkOptions type
## [1.2.6] - 2021-04-21
+
- Add setPlaybackRate(), showControls(), hideControls() & getPlaybackRate methods
-- Add new player events
+- Add new player events
## [1.2.5] - 2021-03-16
+
- Add loadConfig method
- Add "hideTitle" & "token" options
## [1.2.4] - 2021-02-25
+
- Add "hideControls" and "loop" options
## [1.2.3] - 2021-02-02
+
- Fix a string/number comparaison
## [1.2.2] - 2021-02-02
+
- getPaused, getMuted, getDuration, getCurrentTime, getVolume and getLoop now return promises
- Add some unit test
## [1.2.1] - 2021-01-15
-- Add the possibility to specify metadata
\ No newline at end of file
+
+- Add the possibility to specify metadata
diff --git a/README.md b/README.md
index 07a7007..7509aad 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,12 @@
+
[![badge](https://img.shields.io/twitter/follow/api_video?style=social)](https://twitter.com/intent/follow?screen_name=api_video) [![badge](https://img.shields.io/github/stars/apivideo/api.video-player-sdk?style=social)](https://github.com/apivideo/api.video-player-sdk) [![badge](https://img.shields.io/discourse/topics?server=https%3A%2F%2Fcommunity.api.video)](https://community.api.video)
![](https://github.com/apivideo/.github/blob/main/assets/apivideo_banner.png)
+
api.video player SDK
[api.video](https://api.video) is the video infrastructure for product builders. Lightning fast video APIs for integrating, scaling, and managing on-demand & low latency live streaming features in your app.
-
## Table of contents
- [Table of contents](#table-of-contents)
@@ -21,15 +22,15 @@
- [Methods](#methods)
- [Full example](#full-example)
- [Control an existing embedded player using the SDK](#control-an-existing-embedded-player-using-the-sdk)
-
-
-
+
## Project description
SDK to control and interact with the api.video HTML5 Player
-
## Getting started
### Installation
#### Method #1: requirejs
-If you use requirejs you can add the SDK as a dependency to your project with
+If you use requirejs you can add the SDK as a dependency to your project with
```sh
$ npm install --save @api.video/player-sdk
```
-You can then use the SDK in your script:
+You can then use the SDK in your script:
```javascript
-var { PlayerSdk } = require('@api.video/player-sdk');
+var { PlayerSdk } = require("@api.video/player-sdk");
var sdk = new PlayerSdk("#target", {
- id: "",
- // ... other optional options
-});
+ id: "",
+ // ... other optional options
+});
```
#### Method #2: typescript
-If you use Typescript you can add the SDK as a dependency to your project with
+If you use Typescript you can add the SDK as a dependency to your project with
```sh
$ npm install --save @api.video/player-sdk
```
-You can then use the SDK in your script:
+You can then use the SDK in your script:
```typescript
-import { PlayerSdk } from '@api.video/player-sdk'
+import { PlayerSdk } from "@api.video/player-sdk";
const sdk = new PlayerSdk("#target", {
- id: "",
- // ... other optional options
+ id: "",
+ // ... other optional options
});
```
-
#### Method #2: simple include in a javascript project
Include the SDK in your HTML file like so:
```html
- ...
-
+ ...
+
```
Then, once the `window.onload` event has been triggered, create your player using `new PlayerSdk()`:
+
```html
```
## Documentation
### Instantiation
-
+
The PlayerSdk constructor takes 2 parameters:
+
- `targetSelector: string | Element` a CSS selector targeting the DOM element in which you want to create the player (eg. "#target"), or the DOM element itself
- `options: SdkOptions` an object containing the player options. The available options are the following:
-
-| Option name | Mandatory | Type | Description |
-| -------------: | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
-| id | **yes** | string | the id of the video (videoId or liveStreamId) |
-| token | yes for private video | string | the [private video](https://api.video/blog/tutorials/tutorial-private-videos/) url token |
-| privateSession | no | string | the [private video](https://api.video/blog/tutorials/tutorial-private-videos/) session id if needed |
-| live | no (default: false) | boolean | indicate that the video is a live one |
-| autoplay | no (default: false) | boolean | start playing the video as soon as it is loaded |
-| muted | no (default: false) | boolean | the video is muted |
-| metadata | no (default: empty) | object | object containing [metadata](https://api.video/blog/tutorials/dynamic-metadata/) (see **Full example** below) |
-| hideControls | no (default: false) | boolean | the controls are hidden (except unmute button if the video starts muted) |
-| chromeless | no (default: false) | boolean | chromeless mode: all controls are hidden |
-| hideTitle | no (default: false) | boolean | the video title is hidden |
-| hidePoster | no (default: false) | boolean | the poster image isn't displayed |
-| showSubtitles | no (default: false) | boolean | the video subtitles are shown by default |
-| loop | no (default: false) | boolean | once the video is finished it automatically starts again |
-| playbackRate | no (default: 1) | number | the playback rate of the video: 1 for normal, 2 for x2, etc. |
-| sequence | no | {start: number, end: number} | define a sequence of the video to play. The video will start at the `start` timecode and end at the `end` timecode. The timecodes are in seconds. |
-| ads | no | {adTagUrl: string} | see below [ads](#ads) |
-
+| Option name | Mandatory | Type | Description |
+| -------------: | --------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
+| id | **yes** | string | the id of the video (videoId or liveStreamId) |
+| token | yes for private video | string | the [private video](https://api.video/blog/tutorials/tutorial-private-videos/) url token |
+| privateSession | no | string | the [private video](https://api.video/blog/tutorials/tutorial-private-videos/) session id if needed |
+| live | no (default: false) | boolean | indicate that the video is a live one |
+| autoplay | no (default: false) | boolean | start playing the video as soon as it is loaded |
+| muted | no (default: false) | boolean | the video is muted |
+| metadata | no (default: empty) | object | object containing [metadata](https://api.video/blog/tutorials/dynamic-metadata/) (see **Full example** below) |
+| hideControls | no (default: false) | boolean | the controls are hidden (except unmute button if the video starts muted) |
+| chromeless | no (default: false) | boolean | chromeless mode: all controls are hidden |
+| hideTitle | no (default: false) | boolean | the video title is hidden |
+| hidePoster | no (default: false) | boolean | the poster image isn't displayed |
+| showSubtitles | no (default: false) | boolean | the video subtitles are shown by default |
+| loop | no (default: false) | boolean | once the video is finished it automatically starts again |
+| playbackRate | no (default: 1) | number | the playback rate of the video: 1 for normal, 2 for x2, etc. |
+| sequence | no | {start: number, end: number} | define a sequence of the video to play. The video will start at the `start` timecode and end at the `end` timecode. The timecodes are in seconds. |
+| ads | no | {adTagUrl: string} | see below [ads](#ads) |
+| customDomain | no | string | if you've enabled Custom Domains for your account, the complete 'embed' domain (eg. embed.mydomain.com) |
The sdk instance can be used to control the video playback, and to listen to player events.
#### Ads
+
Ads can be displayed in the player. To do so, you need to pass the `ads` option to the sdk constructor. In the `ads` object, pass the `adTagUrl` property with the url of the ad tag. The ad tag must be a VAST 2.0 or 3.0 url. For more information about VAST, check the [IAB documentation](https://www.iab.com/guidelines/vast/).
Note: ads are displayed using the [Google IMA SDK](https://developers.google.com/interactive-media-ads/docs/sdks/html5/quickstart).
@@ -151,13 +153,15 @@ The sdk instance has the following methods:
Load a new video in the same instance of the player. Available options are the same as the ones passed to the SDK constructor (see available).
> Example:
+
```javascript
- player.loadConfig({
- id: "",
- hideTitle: true,
- hideControls: true,
- });
-```
+player.loadConfig({
+ id: "",
+ hideTitle: true,
+ hideControls: true,
+});
+```
+
**`play()`**
Start playing the video.
@@ -178,54 +182,86 @@ Unmute the video.
Hide the player controls.
-
`controls` parameter type definition:
-```typescript
-type ControlName = "play" | "seekBackward" | "seekForward" | "playbackRate"
- | "volume" | "fullscreen" | "subtitles" | "chapters"
- | "pictureInPicture" | "progressBar" | "chromecast" | "download" | "more";
+
+```typescript
+type ControlName =
+ | "play"
+ | "seekBackward"
+ | "seekForward"
+ | "playbackRate"
+ | "volume"
+ | "fullscreen"
+ | "subtitles"
+ | "chapters"
+ | "pictureInPicture"
+ | "progressBar"
+ | "chromecast"
+ | "download"
+ | "more";
```
+
> If no value is provided for the "controls" parameter, all controls will be hidden.
**Note**: the only control that can still be visible is the unmute button if the video as started muted. To hide all controls, including this one, use the setChromeless() method
Example:
+
```javascript
- player.hideControls();
-```
+player.hideControls();
+```
+
> If a list of control names if provided, the associated controls will be hidden.
Example:
+
```javascript
- player.showControls(); // display all controls ...
- player.hideControls(["download", "subtitles"]); // ... except "download" and "subtitles"
-```
+player.showControls(); // display all controls ...
+player.hideControls(["download", "subtitles"]); // ... except "download" and "subtitles"
+```
+
**`showControls(controls?: ControlName[])`**
Show the player controls.
-
`controls` parameter type definition:
-```typescript
-type ControlName = "play" | "seekBackward" | "seekForward" | "playbackRate"
- | "volume" | "fullscreen" | "subtitles" | "chapters"
- | "pictureInPicture" | "progressBar" | "chromecast" | "download" | "more";
+
+```typescript
+type ControlName =
+ | "play"
+ | "seekBackward"
+ | "seekForward"
+ | "playbackRate"
+ | "volume"
+ | "fullscreen"
+ | "subtitles"
+ | "chapters"
+ | "pictureInPicture"
+ | "progressBar"
+ | "chromecast"
+ | "download"
+ | "more";
```
+
> If no value is provided for the "controls" parameter, all controls will be displayed.
Example:
+
```javascript
- player.showControls();
-```
+player.showControls();
+```
+
> If a list of control names if provided, the associated controls will be displayed.
Example:
+
```javascript
- player.hideControls(); // hide all controls ...
- player.showControls(["download", "subtitles"]); // ... except "download" and "subtitles" ...
- // ...
- player.showControls(["progressBar"]); // ... and the progress bar
-```
+player.hideControls(); // hide all controls ...
+player.showControls(["download", "subtitles"]); // ... except "download" and "subtitles" ...
+// ...
+player.showControls(["progressBar"]); // ... and the progress bar
+```
+
**`setChromeless(chromeless: boolean)`**
Define if the player should be in chromeless mode (all controls hidden).
@@ -252,84 +288,92 @@ Define if the video should be played in loop.
**`setAutoplay(autoplay: boolean)`**
-Define if the video should start playing as soon as it is loaded
+Define if the video should start playing as soon as it is loaded
**`seek(time: number)`**
-Add/substract the given number of seconds to/from the playback time.
+Add/substract the given number of seconds to/from the playback time.
**`setPlaybackRate(rate: number)`**
-Set the current playback rate.
+Set the current playback rate.
Example:
+
```javascript
- player.setPlaybackRate(2); // Play at 2x rate
+player.setPlaybackRate(2); // Play at 2x rate
```
+
**`setCurrentTime(time: number)`**
-Set the current playback time (seconds).
+Set the current playback time (seconds).
+
+> Example:
->Example:
```javascript
- player.setCurrentTime(24); // Go the 24th second
-```
+player.setCurrentTime(24); // Go the 24th second
+```
+
**`setVolume(volume: number)`**
Change the audio volume to the given value. From 0 to 1 (0 = muted, 1 = 100%).
> Example:
+
```javascript
- player.setVolume(0.75); // Set the volume to 75%
-```
+player.setVolume(0.75); // Set the volume to 75%
+```
+
**`setVideoStyleObjectFit(value: "contain" | "cover" | "fill" | "none" | "scale-down")`**
Change the [object-fit](https://developer.mozilla.org/fr/docs/Web/CSS/object-fit) CSS value of the video tag.
-
Example:
+
```javascript
- player.setVideoStyleObjectFit("cover"); // Set the object-fit to cover
-```
+player.setVideoStyleObjectFit("cover"); // Set the object-fit to cover
+```
**`setVideoStyleTransform(value: string)`**
Change the [transform](https://developer.mozilla.org/fr/docs/Web/CSS/transform) CSS value of the video tag.
-
Example:
+
```javascript
- player.setVideoStyleTransform("rotateY(180deg)"); // Apply a 180deg rotation around the Y axis (mirroring)
-```
+player.setVideoStyleTransform("rotateY(180deg)"); // Apply a 180deg rotation around the Y axis (mirroring)
+```
**`setTheme(theme: PlayerTheme)`**
Change the appearance of the player.
-
`theme` parameter type definition:
+
```typescript
type PlayerTheme = {
- text?: string;
- link?: string;
- linkHover?: string;
- trackPlayed?: string;
- trackUnplayed?: string;
- trackBackground?: string;
- backgroundTop?: string;
- backgroundBottom?: string;
- backgroundText?: string;
- linkActive?: string;
-}
+ text?: string;
+ link?: string;
+ linkHover?: string;
+ trackPlayed?: string;
+ trackUnplayed?: string;
+ trackBackground?: string;
+ backgroundTop?: string;
+ backgroundBottom?: string;
+ backgroundText?: string;
+ linkActive?: string;
+};
```
+
> Example:
+
```javascript
- player.setTheme({
- link: "red",
- linkHover: "rgba(0, 255, 0, 1)",
- backgroundBottom: "#0000ff",
- });
-```
+player.setTheme({
+ link: "red",
+ linkHover: "rgba(0, 255, 0, 1)",
+ backgroundBottom: "#0000ff",
+});
+```
**`requestFullscreen()`**
@@ -337,7 +381,7 @@ Request fullscreen mode (this may not work in some cases depending on browser re
**`exitFullscreen()`**
-Leave fullscreen mode
+Leave fullscreen mode
**`requestPictureInPicture()`**
@@ -345,7 +389,7 @@ Request picture in picture mode (this may not work in some cases depending on br
**`exitPictureInPicture()`**
-Leave picture in picture mode
+Leave picture in picture mode
**`getPaused(callback?: (paused: boolean) => void): Promise`**
@@ -389,92 +433,96 @@ Destroy the player instance.
**`addEventListener(event: string, callback: () => void)`**
-Define a callback function that will be called when the given event is triggered by the player.
-
+Define a callback function that will be called when the given event is triggered by the player.
Available events are the following:
-Event name | Description | Parameter
----: | --- | ---
-airplayConnected | Started to play on an airplay device | -
-airplayDisconnected | Stopped to play on an airplay device | -
-chromecastConnected | Started to play on a chromecast device | -
-chromecastDisconnected | Stopped to play on a chromecast device | -
-controlsdisabled | Controls are now disabled | -
-controlsenabled | Controls are now enabled | -
-ended | The playback as reached the ended of the video | -
-error | An error occured | -
-firstplay | The video started to play for the first time | -
-fullscreenchange | The player goes to (or goes back from) full screen | -
-mouseenter | The user's mouse entered the player area | -
-mouseleave | The user's mouse leaved the player area | -
-pause | The video has been paused | -
-play | The video started to play (for the first time or after having been paused) | -
-playerresize | The player size has changed | -
-qualitychange | The video quality has changed | `{ resolution: { height: number, width: number } }`
-ratechange | The playback rate has changed | -
-ready | The player is ready to play | -
-resize | The video size has changed
-seeking | The player is seeking | -
-timeupdate | The playback time has changed | `{ currentTime: number }`
-useractive | The user is active | -
-userinactive | The user is inactive | -
-volumechange | The volume has changed | `{ volume: number }`
+| Event name | Description | Parameter |
+| ---------------------: | -------------------------------------------------------------------------- | --------------------------------------------------- |
+| airplayConnected | Started to play on an airplay device | - |
+| airplayDisconnected | Stopped to play on an airplay device | - |
+| chromecastConnected | Started to play on a chromecast device | - |
+| chromecastDisconnected | Stopped to play on a chromecast device | - |
+| controlsdisabled | Controls are now disabled | - |
+| controlsenabled | Controls are now enabled | - |
+| ended | The playback as reached the ended of the video | - |
+| error | An error occured | - |
+| firstplay | The video started to play for the first time | - |
+| fullscreenchange | The player goes to (or goes back from) full screen | - |
+| mouseenter | The user's mouse entered the player area | - |
+| mouseleave | The user's mouse leaved the player area | - |
+| pause | The video has been paused | - |
+| play | The video started to play (for the first time or after having been paused) | - |
+| playerresize | The player size has changed | - |
+| qualitychange | The video quality has changed | `{ resolution: { height: number, width: number } }` |
+| ratechange | The playback rate has changed | - |
+| ready | The player is ready to play | - |
+| resize | The video size has changed |
+| seeking | The player is seeking | - |
+| timeupdate | The playback time has changed | `{ currentTime: number }` |
+| useractive | The user is active | - |
+| userinactive | The user is inactive | - |
+| volumechange | The volume has changed | `{ volume: number }` |
Examples:
+
```javascript
- // listen to the 'play' event
- player.addEventListener('play', function() {
- console.log('play event received');
- });
+// listen to the 'play' event
+player.addEventListener("play", function () {
+ console.log("play event received");
+});
- player.addEventListener('qualitychange', function(ev) {
- console.log(`quality has changed: ${ev.resolution.width}x${ev.resolution.height}`);
- });
+player.addEventListener("qualitychange", function (ev) {
+ console.log(
+ `quality has changed: ${ev.resolution.width}x${ev.resolution.height}`
+ );
+});
```
### Full example
```html
-
- ...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ ...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
```
@@ -484,21 +532,27 @@ It's also possible to integrate the SDK in a page that already contains an embed
```html
-
- ...
-
-
-
- ...
-
-
- ...
-
+
+ ...
+
+
+
+ ...
+
+
+ ...
+
```
To attach the SDK to this player, you'll have to make the following changed in your page:
-
+
- import the `sdk.js` script in your page,
- create a `PlayerSdk` instance once the page is loaded.
@@ -506,25 +560,32 @@ Here is how the page will look like with these changes :
```html
-
- ...
-
-
-
-
- ...
-
-
- ...
-
-
-
+
+ ...
+
+
+
+
+ ...
+
+
+ ...
+
+
+
```
diff --git a/index.ts b/index.ts
index 603998e..a45e5d5 100644
--- a/index.ts
+++ b/index.ts
@@ -1,527 +1,660 @@
type UserEventListener = {
- event: string;
- callback: (data?: any) => void;
-}
+ event: string;
+ callback: (data?: any) => void;
+};
type PlayerEvent = {
- type: string;
- data: any;
-}
+ type: string;
+ data: any;
+};
export type PlayerTheme = {
- text?: string;
- link?: string;
- linkHover?: string;
- trackPlayed?: string;
- trackUnplayed?: string;
- trackBackground?: string;
- backgroundTop?: string;
- backgroundBottom?: string;
- backgroundText?: string;
- linkActive?: string;
-}
+ text?: string;
+ link?: string;
+ linkHover?: string;
+ trackPlayed?: string;
+ trackUnplayed?: string;
+ trackBackground?: string;
+ backgroundTop?: string;
+ backgroundBottom?: string;
+ backgroundText?: string;
+ linkActive?: string;
+};
type AdsOptions = {
- adTagUrl: string;
-}
+ adTagUrl: string;
+};
type SdkOptions = {
- id: string;
- live?: boolean;
- autoplay?: boolean;
- muted?: boolean;
- metadata?: {
- [key: string]: string;
- }
- hideControls?: boolean;
- hidePoster?: boolean;
- chromeless?: boolean;
- loop?: boolean;
- hideTitle?: boolean;
- iframeUrl?: string;
- token?: string;
- privateSession?: string;
- showSubtitles?: boolean;
- ads?: AdsOptions;
- playbackRate?: number;
- sequence?: {
- start: number;
- end: number;
- }
- // This feature is experimental.
- // It may change or be removed at any time and could cause significant playback issues.
- minimalQuality?: number;
- // This feature is experimental.
- // It may change or be removed at any time and could cause significant playback issues.
- maximalQuality?: number;
-}
+ id: string;
+ live?: boolean;
+ autoplay?: boolean;
+ muted?: boolean;
+ metadata?: {
+ [key: string]: string;
+ };
+ hideControls?: boolean;
+ hidePoster?: boolean;
+ chromeless?: boolean;
+ loop?: boolean;
+ hideTitle?: boolean;
+ iframeUrl?: string;
+ token?: string;
+ privateSession?: string;
+ showSubtitles?: boolean;
+ ads?: AdsOptions;
+ playbackRate?: number;
+ sequence?: {
+ start: number;
+ end: number;
+ };
+ customDomain?: string;
+ // This feature is experimental.
+ // It may change or be removed at any time and could cause significant playback issues.
+ minimalQuality?: number;
+ // This feature is experimental.
+ // It may change or be removed at any time and could cause significant playback issues.
+ maximalQuality?: number;
+};
type PlayerSdkEvent = {
- airplayConnected: () => void;
- airplayDisconnected: () => void;
- chromecastConnected: () => void;
- chromecastDisconnected: () => void;
- controlsdisabled: () => void;
- controlsenabled: () => void;
- ended: () => void;
- error: () => void;
- firstplay: () => void;
- fullscreenchange: () => void;
- mouseenter: () => void;
- mouseleave: () => void;
- pause: () => void;
- play: () => void;
- playerresize: () => void;
- qualitychange: (value: { resolution: { height: number, width: number }}) => void;
- ratechange: () => void;
- ready: () => void;
- resize: () => void;
- seeking: () => void;
- timeupdate: (value: { currentTime: number}) => void;
- useractive: () => void;
- userinactive: () => void;
- volumechange: (value: { volume: number}) => void;
-}
-
-export type ControlName = "play" | "seekBackward" | "seekForward" | "playbackRate"
- | "volume" | "fullscreen" | "subtitles" | "chapters"
- | "pictureInPicture" | "progressBar" | "chromecast" | "download" | "more";
-
+ airplayConnected: () => void;
+ airplayDisconnected: () => void;
+ chromecastConnected: () => void;
+ chromecastDisconnected: () => void;
+ controlsdisabled: () => void;
+ controlsenabled: () => void;
+ ended: () => void;
+ error: () => void;
+ firstplay: () => void;
+ fullscreenchange: () => void;
+ mouseenter: () => void;
+ mouseleave: () => void;
+ pause: () => void;
+ play: () => void;
+ playerresize: () => void;
+ qualitychange: (value: {
+ resolution: { height: number; width: number };
+ }) => void;
+ ratechange: () => void;
+ ready: () => void;
+ resize: () => void;
+ seeking: () => void;
+ timeupdate: (value: { currentTime: number }) => void;
+ useractive: () => void;
+ userinactive: () => void;
+ volumechange: (value: { volume: number }) => void;
+};
+
+export type ControlName =
+ | "play"
+ | "seekBackward"
+ | "seekForward"
+ | "playbackRate"
+ | "volume"
+ | "fullscreen"
+ | "subtitles"
+ | "chapters"
+ | "pictureInPicture"
+ | "progressBar"
+ | "chromecast"
+ | "download"
+ | "more";
export class PlayerSdk {
- private static DEFAULT_IFRAME_URL = "https://embed.api.video/${type}/${id}";
-
- private iframe: HTMLIFrameElement | null = null;
- private sdkInSync: boolean = false;
- private currentVideoReady: boolean = false;
- private onceSdkInSyncCallbacks: (() => void)[] = [];
- private onceVideoReadyCallbacks: (() => void)[] = [];
- private userEventListeners: UserEventListener[] = [];
- private sdkPlayerId: number;
- private sdkOrigin: string;
- private playerOrigin: string | null = null;
- private postMessageCallbacks: { [callbackId: string]: (arg: any) => void } = {};
- private iframeUrl: string;
- private options: SdkOptions;
-
- static nextSdkPlayerId: number = 1;
-
- constructor(targetSelector: string | Element, userOptions?: SdkOptions) {
- this.sdkPlayerId = PlayerSdk.nextSdkPlayerId++;
-
- this.sdkOrigin = `${window.location.protocol}//${window.location.host}`;
-
- const target = targetSelector instanceof Element
- ? targetSelector
- : document.querySelector(targetSelector);
-
- if (target == null) {
- throw new Error("No match found for selector " + targetSelector);
- }
-
- this.iframe = target.tagName !== "IFRAME"
- ? this.createIframe(target)
- : target as HTMLIFrameElement
-
- const options = userOptions || {} as SdkOptions;
- this.iframeUrl = options.iframeUrl || PlayerSdk.DEFAULT_IFRAME_URL;
-
- if (!this.iframe.src) {
- this.createNewPlayer(this.iframe, options)
- } else {
- this.bindExistingPlayer(this.iframe);
- }
-
- this.onceSdkInSyncCallbacks = [];
- this.userEventListeners = [];
- this.sdkInSync = false;
- this.currentVideoReady = false;
- this.playerOrigin = new URL(this.iframeUrl).origin;
- this.options = options;
-
- window.addEventListener("message", (message) => {
- if (message.origin === this.playerOrigin && parseInt(message.data?.sdkPlayerId, 10) === this.sdkPlayerId) {
- if (!!message.data.callbackId && !!this.postMessageCallbacks[message.data.callbackId]) {
- this.postMessageCallbacks[message.data.callbackId](message.data.arg);
- } else {
- this.onEvent(message.data);
- }
- }
- }, false);
-
- if(options.playbackRate) {
- this.setPlaybackRate(options.playbackRate);
+ private iframe: HTMLIFrameElement | null = null;
+ private sdkInSync: boolean = false;
+ private currentVideoReady: boolean = false;
+ private onceSdkInSyncCallbacks: (() => void)[] = [];
+ private onceVideoReadyCallbacks: (() => void)[] = [];
+ private userEventListeners: UserEventListener[] = [];
+ private sdkPlayerId: number;
+ private sdkOrigin: string;
+ private playerOrigin: string | null = null;
+ private postMessageCallbacks: { [callbackId: string]: (arg: any) => void } =
+ {};
+ private iframeUrl: string;
+ private options: SdkOptions;
+
+ static nextSdkPlayerId: number = 1;
+
+ constructor(targetSelector: string | Element, userOptions?: SdkOptions) {
+ this.sdkPlayerId = PlayerSdk.nextSdkPlayerId++;
+
+ this.sdkOrigin = `${window.location.protocol}//${window.location.host}`;
+
+ const target =
+ targetSelector instanceof Element
+ ? targetSelector
+ : document.querySelector(targetSelector);
+
+ if (target == null) {
+ throw new Error("No match found for selector " + targetSelector);
+ }
+
+ this.iframe =
+ target.tagName !== "IFRAME"
+ ? this.createIframe(target)
+ : (target as HTMLIFrameElement);
+
+ const options = userOptions || ({} as SdkOptions);
+ this.iframeUrl = this.getIframeUrl(options);
+
+ if (!this.iframe.src) {
+ this.createNewPlayer(this.iframe, options);
+ } else {
+ this.bindExistingPlayer(this.iframe);
+ }
+
+ this.onceSdkInSyncCallbacks = [];
+ this.userEventListeners = [];
+ this.sdkInSync = false;
+ this.currentVideoReady = false;
+ this.playerOrigin = new URL(this.iframeUrl).origin;
+ this.options = options;
+
+ window.addEventListener(
+ "message",
+ (message) => {
+ if (
+ message.origin === this.playerOrigin &&
+ parseInt(message.data?.sdkPlayerId, 10) === this.sdkPlayerId
+ ) {
+ if (
+ !!message.data.callbackId &&
+ !!this.postMessageCallbacks[message.data.callbackId]
+ ) {
+ this.postMessageCallbacks[message.data.callbackId](
+ message.data.arg
+ );
+ } else {
+ this.onEvent(message.data);
+ }
}
- }
+ },
+ false
+ );
- loadConfig(options: SdkOptions) {
- this.currentVideoReady = false;
- this.options = {
- ...this.options,
- ...options,
- };
- this.postMessage({
- message: 'loadConfig',
- url: this.buildPlayerUrl(this.options)
- });
+ if (options.playbackRate) {
+ this.setPlaybackRate(options.playbackRate);
}
+ }
- play() {
- this.postMessage({ message: 'play' });
+ private getIframeUrl(options: SdkOptions) {
+ if (options.iframeUrl) {
+ return options.iframeUrl;
}
+ const domain = options.customDomain || "embed.api.video";
+ return "https://" + domain + "/${type}/${id}";
+ }
- setVideoStyleTransform(value: string) {
- this.postMessage({ message: 'setStyleProperty', query: "video", property: "transform", value }, undefined, false);
- this.postMessage({ message: 'setStyleProperty', query: ".vjs-poster", property: "transform", value }, undefined, false);
- }
-
- setVideoStyleObjectFit(value: "contain" | "cover" | "fill" | "none" | "scale-down") {
- this.postMessage({ message: 'setStyleProperty', query: "video", property: "object-fit", value }, undefined, false);
- this.postMessage({ message: 'setStyleProperty', query: ".vjs-poster", property: "background-size", value }, undefined, false);
- }
-
- // This feature is experimental.
- // It may change or be removed at any time and could cause significant playback issues.
- setMinimalQuality(quality: number) {
- this.postMessage({ message: "setMinimalQuality", quality})
+ loadConfig(options: SdkOptions) {
+ this.currentVideoReady = false;
+ this.options = {
+ ...this.options,
+ ...options,
};
-
- // This feature is experimental.
- // It may change or be removed at any time and could cause significant playback issues.
- setMaximalQuality(quality: number) {
- this.postMessage({ message: "setMaximalQuality", quality})
+ this.postMessage({
+ message: "loadConfig",
+ url: this.buildPlayerUrl(this.options),
+ });
+ }
+
+ play() {
+ this.postMessage({ message: "play" });
+ }
+
+ setVideoStyleTransform(value: string) {
+ this.postMessage(
+ {
+ message: "setStyleProperty",
+ query: "video",
+ property: "transform",
+ value,
+ },
+ undefined,
+ false
+ );
+ this.postMessage(
+ {
+ message: "setStyleProperty",
+ query: ".vjs-poster",
+ property: "transform",
+ value,
+ },
+ undefined,
+ false
+ );
+ }
+
+ setVideoStyleObjectFit(
+ value: "contain" | "cover" | "fill" | "none" | "scale-down"
+ ) {
+ this.postMessage(
+ {
+ message: "setStyleProperty",
+ query: "video",
+ property: "object-fit",
+ value,
+ },
+ undefined,
+ false
+ );
+ this.postMessage(
+ {
+ message: "setStyleProperty",
+ query: ".vjs-poster",
+ property: "background-size",
+ value: value !== "fill" ? value : "100% 100%",
+ },
+ undefined,
+ false
+ );
+ }
+
+ // This feature is experimental.
+ // It may change or be removed at any time and could cause significant playback issues.
+ setMinimalQuality(quality: number) {
+ this.postMessage({ message: "setMinimalQuality", quality });
+ }
+
+ // This feature is experimental.
+ // It may change or be removed at any time and could cause significant playback issues.
+ setMaximalQuality(quality: number) {
+ this.postMessage({ message: "setMaximalQuality", quality });
+ }
+
+ hideControls(controls?: ControlName[]) {
+ if (!controls) {
+ this.options.hideControls = true;
+ }
+ this.postMessage({ message: "hideControls", controls }, undefined, true);
+ }
+ showControls(controls?: ControlName[]) {
+ if (!controls) {
+ this.options.hideControls = false;
+ }
+ this.postMessage({ message: "showControls", controls }, undefined, true);
+ }
+ hideSubtitles() {
+ this.options.showSubtitles = false;
+ this.postMessage({ message: "hideSubtitles" });
+ }
+ showSubtitles() {
+ this.options.showSubtitles = true;
+ this.postMessage({ message: "showSubtitles" });
+ }
+ hideTitle() {
+ this.options.hideTitle = true;
+ this.postMessage({ message: "hideTitle" });
+ }
+ showTitle() {
+ this.options.hideTitle = false;
+ this.postMessage({ message: "showTitle" });
+ }
+ hidePoster() {
+ this.options.hidePoster = true;
+ this.postMessage({ message: "hidePoster" });
+ }
+ showPoster() {
+ this.options.hidePoster = false;
+ this.postMessage({ message: "showPoster" });
+ }
+ pause() {
+ this.postMessage({ message: "pause" });
+ }
+ mute() {
+ this.options.muted = true;
+ this.postMessage({ message: "mute" });
+ }
+ unmute() {
+ this.options.muted = false;
+ this.postMessage({ message: "unmute" });
+ }
+ seek(time: number) {
+ this.postMessage({ message: "seek", seek: time });
+ }
+ setCurrentTime(time: number) {
+ this.postMessage({ message: "setCurrentTime", currentTime: time });
+ }
+ setVolume(volume: number) {
+ this.postMessage({ message: "setVolume", volume });
+ }
+ setAutoplay(autoplay: boolean) {
+ this.options.autoplay = autoplay;
+ this.postMessage({ message: "setAutoplay", autoplay });
+ }
+ setLoop(loop: boolean) {
+ this.options.loop = loop;
+ this.postMessage({ message: "setLoop", loop });
+ }
+ setChromeless(chromeless: boolean) {
+ this.options.chromeless = chromeless;
+ this.postMessage({ message: "setChromeless", chromeless });
+ }
+ setPlaybackRate(rate: number) {
+ this.options.playbackRate = rate;
+ this.postMessage({ message: "setPlaybackRate", rate }, undefined, true);
+ }
+ exitFullscreen() {
+ this.postMessage({ message: "exitFullscreen" }, undefined, true);
+ }
+ requestFullscreen() {
+ this.postMessage({ message: "requestFullscreen" }, undefined, true);
+ }
+ exitPictureInPicture() {
+ this.postMessage({ message: "exitPictureInPicture" }, undefined, true);
+ }
+ requestPictureInPicture() {
+ this.postMessage({ message: "requestPictureInPicture" }, undefined, true);
+ }
+ setTheme(theme: PlayerTheme) {
+ this.postMessage({ message: "setTheme", theme });
+ }
+ getPaused(callback?: (paused: boolean) => void): Promise {
+ return this.postMessage({ message: "getPaused" }, callback);
+ }
+ getMuted(callback?: (muted: boolean) => void): Promise {
+ return this.postMessage({ message: "getMuted" }, callback);
+ }
+ getPlaying(callback?: (muted: boolean) => void): Promise {
+ return this.postMessage({ message: "getPlaying" }, callback);
+ }
+ getDuration(callback?: (duration: number) => void): Promise {
+ return this.postMessage({ message: "getDuration" }, callback, true);
+ }
+ getCurrentTime(callback?: (currentTime: number) => void): Promise {
+ return this.postMessage({ message: "getCurrentTime" }, callback);
+ }
+ getPlaybackRate(callback?: (rate: number) => void): Promise {
+ return this.postMessage({ message: "getPlaybackRate" }, callback);
+ }
+ getVolume(callback?: (volume: number) => void): Promise {
+ return this.postMessage({ message: "getVolume" }, callback);
+ }
+ getLoop(callback?: (loop: boolean) => void): Promise {
+ return this.postMessage({ message: "getLoop" }, callback);
+ }
+ getVideoSize(
+ callback?: (size: { width: number; height: number }) => void
+ ): Promise<{ width: number; height: number }> {
+ return this.postMessage({ message: "getVideoSize" }, callback, true);
+ }
+ isLiveStream(callback?: (isLiveStream: boolean) => void): Promise {
+ return this.postMessage({ message: "isLiveStream" }, callback);
+ }
+
+ addEventListener(
+ event: K,
+ callback: PlayerSdkEvent[K]
+ ) {
+ this.userEventListeners.push({ event, callback });
+ }
+
+ destroy() {
+ this.postMessage({ message: "destroy" });
+ setTimeout(() => this.iframe?.parentElement?.removeChild(this.iframe), 0);
+ }
+
+ private createNewPlayer(iframe: HTMLIFrameElement, options: SdkOptions) {
+ this.setIframeSrc(iframe, this.buildPlayerUrl(options));
+ }
+
+ private buildPlayerUrl(options: SdkOptions) {
+ if (!options.id) {
+ throw new Error("Missing id in options");
+ }
+
+ const url = this.iframeUrl
+ .replace("${id}", options.id)
+ .replace("${type}", options.live ? "live" : "vod");
+
+ return this.addParametersInIframeHash(
+ `${url}?${this.urlParametersFromOptions(options)}`,
+ options
+ );
+ }
+
+ private bindExistingPlayer(iframe: HTMLIFrameElement) {
+ this.setIframeSrc(
+ iframe,
+ this.addParametersInIframeHash(iframe.src, {} as SdkOptions)
+ );
+ }
+
+ private addParametersInIframeHash(url: string, options: SdkOptions) {
+ const addParameterInIframeHash = (parameter: string, value?: string) => {
+ const indexOfHash = url.indexOf("#");
+ const parameterAndValue = value ? `${parameter}:${value}` : parameter;
+
+ if (indexOfHash === -1) {
+ return `${url}#${parameterAndValue}`;
+ }
+ const beforeHash = url.substr(0, indexOfHash);
+ let afterHash = url.substr(indexOfHash + 1);
+
+ afterHash = afterHash.replace(new RegExp(`${parameter}(:[^;]+)?;?`), "");
+
+ return `${beforeHash}#${parameterAndValue};${afterHash}`;
};
- hideControls(controls?: ControlName[]) {
- if(!controls) {
- this.options.hideControls = true;
- }
- this.postMessage({ message: 'hideControls', controls }, undefined, true);
- }
- showControls(controls?: ControlName[]) {
- if(!controls) {
- this.options.hideControls = false;
- }
- this.postMessage({ message: 'showControls', controls }, undefined, true);
- }
- hideSubtitles() {
- this.options.showSubtitles = false;
- this.postMessage({ message: 'hideSubtitles' });
- }
- showSubtitles() {
- this.options.showSubtitles = true;
- this.postMessage({ message: 'showSubtitles' });
- }
- hideTitle() {
- this.options.hideTitle = true;
- this.postMessage({ message: 'hideTitle' });
- }
- showTitle() {
- this.options.hideTitle = false;
- this.postMessage({ message: 'showTitle' });
- }
- hidePoster() {
- this.options.hidePoster = true;
- this.postMessage({ message: 'hidePoster' });
- }
- showPoster() {
- this.options.hidePoster = false;
- this.postMessage({ message: 'showPoster' });
- }
- pause() {
- this.postMessage({ message: 'pause' });
- }
- mute() {
- this.options.muted = true;
- this.postMessage({ message: 'mute' });
- }
- unmute() {
- this.options.muted = false;
- this.postMessage({ message: 'unmute' });
- }
- seek(time: number) {
- this.postMessage({ message: 'seek', seek: time });
- }
- setCurrentTime(time: number) {
- this.postMessage({ message: 'setCurrentTime', currentTime: time });
- }
- setVolume(volume: number) {
- this.postMessage({ message: 'setVolume', volume });
- }
- setAutoplay(autoplay: boolean) {
- this.options.autoplay = autoplay;
- this.postMessage({ message: 'setAutoplay', autoplay });
- }
- setLoop(loop: boolean) {
- this.options.loop = loop;
- this.postMessage({ message: 'setLoop', loop });
- }
- setChromeless(chromeless: boolean) {
- this.options.chromeless = chromeless;
- this.postMessage({ message: 'setChromeless', chromeless });
- }
- setPlaybackRate(rate: number) {
- this.options.playbackRate = rate;
- this.postMessage({ message: 'setPlaybackRate', rate }, undefined, true);
- }
- exitFullscreen() {
- this.postMessage({ message: 'exitFullscreen' }, undefined, true);
- }
- requestFullscreen() {
- this.postMessage({ message: 'requestFullscreen' }, undefined, true);
- }
- exitPictureInPicture() {
- this.postMessage({ message: 'exitPictureInPicture' }, undefined, true);
- }
- requestPictureInPicture() {
- this.postMessage({ message: 'requestPictureInPicture' }, undefined, true);
- }
- setTheme(theme: PlayerTheme) {
- this.postMessage({ message: 'setTheme', theme });
- }
- getPaused(callback?: (paused: boolean) => void): Promise {
- return this.postMessage({ message: 'getPaused' }, callback);
- }
- getMuted(callback?: (muted: boolean) => void): Promise {
- return this.postMessage({ message: 'getMuted' }, callback);
- }
- getPlaying(callback?: (muted: boolean) => void): Promise {
- return this.postMessage({ message: 'getPlaying' }, callback);
- }
- getDuration(callback?: (duration: number) => void): Promise {
- return this.postMessage({ message: 'getDuration' }, callback, true);
- }
- getCurrentTime(callback?: (currentTime: number) => void): Promise {
- return this.postMessage({ message: 'getCurrentTime' }, callback);
- }
- getPlaybackRate(callback?: (rate: number) => void): Promise {
- return this.postMessage({ message: 'getPlaybackRate' }, callback);
- }
- getVolume(callback?: (volume: number) => void): Promise {
- return this.postMessage({ message: 'getVolume' }, callback);
- }
- getLoop(callback?: (loop: boolean) => void): Promise {
- return this.postMessage({ message: 'getLoop' }, callback);
- }
- getVideoSize(callback?: (size: {width: number, height: number}) => void): Promise<{width: number, height: number}> {
- return this.postMessage({ message: 'getVideoSize' }, callback, true);
- }
- isLiveStream(callback?: (isLiveStream: boolean) => void): Promise {
- return this.postMessage({ message: 'isLiveStream' }, callback);
- }
+ url = addParameterInIframeHash("sdkPlayerId", "" + this.sdkPlayerId);
+ url = addParameterInIframeHash("sdkOrigin", btoa(this.sdkOrigin));
+ url = addParameterInIframeHash("api");
- addEventListener(event: K, callback: PlayerSdkEvent[K]) {
- this.userEventListeners.push({ event, callback });
+ if (options.hideControls === true) {
+ url = addParameterInIframeHash("hide-controls");
}
- destroy() {
- this.postMessage({ message: 'destroy' });
- setTimeout(() => this.iframe?.parentElement?.removeChild(this.iframe), 0);
+ if (options.chromeless === true) {
+ url = addParameterInIframeHash("chromeless");
}
- private createNewPlayer(iframe: HTMLIFrameElement, options: SdkOptions) {
- this.setIframeSrc(iframe, this.buildPlayerUrl(options));
+ if (options.hidePoster === true) {
+ url = addParameterInIframeHash("hide-poster");
}
- private buildPlayerUrl(options: SdkOptions) {
- if (!options.id) {
- throw new Error("Missing id in options");
- }
-
- const url = this.iframeUrl
- .replace("${id}", options.id)
- .replace("${type}", options.live ? "live" : "vod");
-
- return this.addParametersInIframeHash(`${url}?${this.urlParametersFromOptions(options)}`, options);
+ if (options.loop === true) {
+ url = addParameterInIframeHash("loop");
}
- private bindExistingPlayer(iframe: HTMLIFrameElement) {
- this.setIframeSrc(iframe, this.addParametersInIframeHash(iframe.src, {} as SdkOptions));
+ if (options.hideTitle === true) {
+ url = addParameterInIframeHash("hide-title");
}
- private addParametersInIframeHash(url: string, options: SdkOptions) {
- const addParameterInIframeHash = (parameter: string, value?: string) => {
- const indexOfHash = url.indexOf("#");
- const parameterAndValue = value ? `${parameter}:${value}` : parameter;
-
- if (indexOfHash === -1) {
- return `${url}#${parameterAndValue}`;
- }
- const beforeHash = url.substr(0, indexOfHash);
- let afterHash = url.substr(indexOfHash + 1);
-
- afterHash = afterHash.replace(new RegExp(`${parameter}(:[^;]+)?;?`), "");
-
- return `${beforeHash}#${parameterAndValue};${afterHash}`;
- };
-
- url = addParameterInIframeHash("sdkPlayerId", "" + this.sdkPlayerId);
- url = addParameterInIframeHash("sdkOrigin", btoa(this.sdkOrigin));
- url = addParameterInIframeHash("api");
-
- if (options.hideControls === true) {
- url = addParameterInIframeHash("hide-controls");
- }
-
- if (options.chromeless === true) {
- url = addParameterInIframeHash("chromeless");
- }
-
- if (options.hidePoster === true) {
- url = addParameterInIframeHash("hide-poster");
- }
-
- if (options.loop === true) {
- url = addParameterInIframeHash("loop");
- }
-
- if (options.hideTitle === true) {
- url = addParameterInIframeHash("hide-title");
- }
-
- if (options.showSubtitles === true) {
- url = addParameterInIframeHash("show-subtitles");
- }
-
- if(options.minimalQuality !== undefined) {
- url = addParameterInIframeHash(`min-quality:${encodeURIComponent(options.minimalQuality)}`);
- }
-
- if(options.maximalQuality !== undefined) {
- url = addParameterInIframeHash(`max-quality:${encodeURIComponent(options.maximalQuality)}`);
- }
-
- if(options.ads?.adTagUrl) {
- url = addParameterInIframeHash(`adTagUrl:${encodeURIComponent(options.ads?.adTagUrl)}`);
- }
-
- if(!isNaN(parseInt(""+options.sequence?.end, 10)) && !isNaN(parseInt(""+options.sequence?.start, 10))) {
- url = addParameterInIframeHash(`t=${options.sequence?.start},${options.sequence?.end}`);
- }
-
- return url;
+ if (options.showSubtitles === true) {
+ url = addParameterInIframeHash("show-subtitles");
}
- private urlParametersFromOptions(options: SdkOptions) {
- const allowedKeys = ["id", "live", "autoplay", "muted", "metadata", "hideControls", "hidePoster",
- "chromeless", "loop", "hideTitle", "iframeUrl", "token", "showSubtitles", "ts","avh"];
-
- const optionsAsAny = options as any;
- optionsAsAny.ts = new Date().getTime();
-
- if(options.privateSession) {
- optionsAsAny.avh = options.privateSession;
- }
-
- return Object.keys(options).map((key: string) => {
- if(allowedKeys.indexOf(key) === -1) {
- return;
- }
- if (key === "metadata" && typeof optionsAsAny[key] === "object") {
- const metadata = optionsAsAny[key];
- return Object.keys(metadata).map((metadataName: string) => {
- return "metadata[" + metadataName + "]=" + metadata[metadataName];
- }).join("&");
- }
- return key + '=' + optionsAsAny[key];
- }).join('&');
+ if (options.minimalQuality !== undefined) {
+ url = addParameterInIframeHash(
+ `min-quality:${encodeURIComponent(options.minimalQuality)}`
+ );
}
- private onEvent(data: PlayerEvent) {
- const userData = { ...data } as any;
- delete userData.type;
- delete userData.sdkPlayerId;
-
- this.userEventListeners
- .filter(uel => uel.event === data.type)
- .forEach(uel => uel.callback(userData));
-
- switch (data.type) {
- case 'ready':
- this.onVideoReady();
- break;
- case 'sdkSync':
- this.onSdkInSync();
- break;
- }
+ if (options.maximalQuality !== undefined) {
+ url = addParameterInIframeHash(
+ `max-quality:${encodeURIComponent(options.maximalQuality)}`
+ );
}
- private onSdkInSync() {
- if (!this.sdkInSync) {
- this.sdkInSync = true;
- this.onceSdkInSyncCallbacks.forEach(cb => {
- cb();
- });
- }
+ if (options.ads?.adTagUrl) {
+ url = addParameterInIframeHash(
+ `adTagUrl:${encodeURIComponent(options.ads?.adTagUrl)}`
+ );
}
- private onVideoReady() {
- if(!this.currentVideoReady) {
- this.currentVideoReady = true;
- this.onceVideoReadyCallbacks.forEach(cb => {
- cb();
- });
- }
+ if (
+ !isNaN(parseInt("" + options.sequence?.end, 10)) &&
+ !isNaN(parseInt("" + options.sequence?.start, 10))
+ ) {
+ url = addParameterInIframeHash(
+ `t=${options.sequence?.start},${options.sequence?.end}`
+ );
}
- private postMessage(message: any, callback?: (arg: T) => void, requireVideoReady: boolean = false): Promise {
-
- return new Promise((resolve, reject): void => {
-
- if (!this.playerOrigin || !this.iframe?.contentWindow) {
- reject();
- return;
- }
-
- const messageWithPlayerId = {
- ...message,
- sdkPlayerId: this.sdkPlayerId
- }
-
- const callbackId = this.makeId(16);
- this.postMessageCallbacks[callbackId] = (res: T) => {
- resolve(res as T);
- if (!!callback) {
- callback(res);
- }
- };
- messageWithPlayerId.callbackId = callbackId;
-
- if(!this.currentVideoReady && requireVideoReady) {
- this.onceVideoReadyCallbacks.push(() => this.playerOrigin && this.iframe?.contentWindow?.postMessage(messageWithPlayerId, this.playerOrigin));
- return;
- }
-
- if (this.sdkInSync && !!this.playerOrigin) {
- this.iframe.contentWindow.postMessage(messageWithPlayerId, this.playerOrigin);
- return;
- }
-
- this.onceSdkInSyncCallbacks.push(() => this.playerOrigin && this.iframe?.contentWindow?.postMessage(messageWithPlayerId, this.playerOrigin));
- });
- }
+ return url;
+ }
- private makeId(length: number) {
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
- const charactersLength = characters.length;
- let result = '';
- for (let i = 0; i < length; i++) {
- result += characters.charAt(Math.floor(Math.random() * charactersLength));
- }
- return result;
- }
+ private urlParametersFromOptions(options: SdkOptions) {
+ const allowedKeys = [
+ "id",
+ "live",
+ "autoplay",
+ "muted",
+ "metadata",
+ "hideControls",
+ "hidePoster",
+ "chromeless",
+ "loop",
+ "hideTitle",
+ "iframeUrl",
+ "token",
+ "showSubtitles",
+ "ts",
+ "avh",
+ ];
- private createIframe(target: Element) {
- const ifr = document.createElement('iframe');
- ifr.style.height = "100%";
- ifr.style.width = "100%";
- ifr.allowFullscreen = true;
- ifr.allow = "autoplay";
- target.appendChild(ifr);
- return ifr;
- }
+ const optionsAsAny = options as any;
+ optionsAsAny.ts = new Date().getTime();
- private setIframeSrc(iframe: HTMLIFrameElement, url: string) {
- iframe.src = url;
+ if (options.privateSession) {
+ optionsAsAny.avh = options.privateSession;
}
-}
\ No newline at end of file
+
+ return Object.keys(options)
+ .map((key: string) => {
+ if (allowedKeys.indexOf(key) === -1) {
+ return;
+ }
+ if (key === "metadata" && typeof optionsAsAny[key] === "object") {
+ const metadata = optionsAsAny[key];
+ return Object.keys(metadata)
+ .map((metadataName: string) => {
+ return "metadata[" + metadataName + "]=" + metadata[metadataName];
+ })
+ .join("&");
+ }
+ return key + "=" + optionsAsAny[key];
+ })
+ .join("&");
+ }
+
+ private onEvent(data: PlayerEvent) {
+ const userData = { ...data } as any;
+ delete userData.type;
+ delete userData.sdkPlayerId;
+
+ this.userEventListeners
+ .filter((uel) => uel.event === data.type)
+ .forEach((uel) => uel.callback(userData));
+
+ switch (data.type) {
+ case "ready":
+ this.onVideoReady();
+ break;
+ case "sdkSync":
+ this.onSdkInSync();
+ break;
+ }
+ }
+
+ private onSdkInSync() {
+ if (!this.sdkInSync) {
+ this.sdkInSync = true;
+ this.onceSdkInSyncCallbacks.forEach((cb) => {
+ cb();
+ });
+ }
+ }
+
+ private onVideoReady() {
+ if (!this.currentVideoReady) {
+ this.currentVideoReady = true;
+ this.onceVideoReadyCallbacks.forEach((cb) => {
+ cb();
+ });
+ }
+ }
+
+ private postMessage(
+ message: any,
+ callback?: (arg: T) => void,
+ requireVideoReady: boolean = false
+ ): Promise {
+ return new Promise((resolve, reject): void => {
+ if (!this.playerOrigin || !this.iframe?.contentWindow) {
+ reject();
+ return;
+ }
+
+ const messageWithPlayerId = {
+ ...message,
+ sdkPlayerId: this.sdkPlayerId,
+ };
+
+ const callbackId = this.makeId(16);
+ this.postMessageCallbacks[callbackId] = (res: T) => {
+ resolve(res as T);
+ if (!!callback) {
+ callback(res);
+ }
+ };
+ messageWithPlayerId.callbackId = callbackId;
+
+ if (!this.currentVideoReady && requireVideoReady) {
+ this.onceVideoReadyCallbacks.push(
+ () =>
+ this.playerOrigin &&
+ this.iframe?.contentWindow?.postMessage(
+ messageWithPlayerId,
+ this.playerOrigin
+ )
+ );
+ return;
+ }
+
+ if (this.sdkInSync && !!this.playerOrigin) {
+ this.iframe.contentWindow.postMessage(
+ messageWithPlayerId,
+ this.playerOrigin
+ );
+ return;
+ }
+
+ this.onceSdkInSyncCallbacks.push(
+ () =>
+ this.playerOrigin &&
+ this.iframe?.contentWindow?.postMessage(
+ messageWithPlayerId,
+ this.playerOrigin
+ )
+ );
+ });
+ }
+
+ private makeId(length: number) {
+ const characters =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ const charactersLength = characters.length;
+ let result = "";
+ for (let i = 0; i < length; i++) {
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
+ }
+ return result;
+ }
+
+ private createIframe(target: Element) {
+ const ifr = document.createElement("iframe");
+ ifr.style.height = "100%";
+ ifr.style.width = "100%";
+ ifr.allowFullscreen = true;
+ ifr.allow = "autoplay";
+ target.appendChild(ifr);
+ return ifr;
+ }
+
+ private setIframeSrc(iframe: HTMLIFrameElement, url: string) {
+ iframe.src = url;
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index c2626c5..e3cbf4f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@api.video/player-sdk",
- "version": "1.2.28",
+ "version": "1.2.29",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@api.video/player-sdk",
- "version": "1.2.28",
+ "version": "1.2.29",
"license": "MIT",
"dependencies": {
"core-js": "^3.8.3",
diff --git a/package.json b/package.json
index 5f01777..b21c855 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@api.video/player-sdk",
- "version": "1.2.28",
+ "version": "1.2.29",
"description": "api.video player SDK",
"repository": {
"type": "git",