Skip to content

Commit

Permalink
Experimental StreamAMG support and build tooling
Browse files Browse the repository at this point in the history
  • Loading branch information
driskell committed Apr 1, 2022
1 parent dacb728 commit 0654673
Show file tree
Hide file tree
Showing 22 changed files with 534 additions and 11 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Publish package to GitHub Packages
on:
release:
types:
- created

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v3
# Setup .npmrc file to publish to GitHub Packages
- uses: actions/setup-node@v3
with:
node-version: 14
cache: yarn
cache-dependency-path: yarn.lock
- run: yarn ci
- run: yarn publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@othermedia:registry=https://npm.pkg.github.com
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Added by OM
{
"typescript.validate.enable": false,
"javascript.validate.enable": false,
"markdown.extension.toc.updateOnSave": false
}
20 changes: 20 additions & 0 deletions OMREADME.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `playkit-js-providers`

Because the upstream player utilises API v3.3.0 and StreamAMG do not support it yet (they're on v3.1.6) a provider replacement is needed. The main differences between the two API versions are as follows:

- v3.3.0 has a baseEntry.getPlaybackContext API that returns access control information and playback sources. This does not exist in v3.1.6. Instead, the access control information is loaded via baseEntry.getContextData and is exactly the same response format as v3.3.0's getPlaybackContext except it has no sources or bumper data. We can ignore bumper data, and simply hard-code in a playManifest URL in any custom provider we build.
- v3.3.0 thumbnail requests seem to be slightly different slicing to what v3.1.6 provides - however, it is possible that StreamAMG have changed their thumbnail responses for their own implementation in their current IFRAME player. So some adjustments are needed if the thumbnail on the scrubber is needed.
- v3.3.0 allows POST requests with a JSON body. v3.1.6 only supports GET requests with the request information specified as query parameters, or a POST request with form data. Therefore a custom provider will need to use that request format. The response format however, is fortunately, the same.

## StreamAMG Provider

This fork mainly adds a new [StreamAMG Provider](src/k-provider/streamamg). It extends the OVP provider which is for Kaltura API v3.3.0 and implements the necessary overrides and extensions to support API v3.1.6, with all of the above differences resolved.

## Additional Changes

- Added `.vscode` to suppress TypeScript and ESLint checks since this is using Flow. You'll likely want to install Flow extension for VSCode.
- Adjusted `src/index.html` that now has StreamAMG endpoints in there for testing.

## Building and Testing

See the main [README](README.md) file for information.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# OTHER MEDIA FORK

See [OMREADME.md](OMREADME.md)

# PlayKit JS Providers - Cloud TV and OVP Media Provider Plugins for the [PlayKit JS Player]

[![Build Status](https://travis-ci.com/kaltura/playkit-js-providers.svg?token=s2ZQw18ukx9Q6ePzDX3F&branch=master)](https://travis-ci.com/kaltura/playkit-js-providers)
Expand Down
2 changes: 1 addition & 1 deletion dist/playkit-ott-provider.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/playkit-ott-provider.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/playkit-ovp-provider.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/playkit-ovp-provider.js.map

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions dist/playkit-streamamg-provider.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/playkit-streamamg-provider.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "playkit-js-providers",
"name": "@othermedia/playkit-js-providers",
"version": "2.28.1",
"description": "",
"scripts": {
Expand All @@ -14,7 +14,6 @@
"eslint": "eslint . --color",
"flow": "flow check",
"commit:dist": "git add --force --all dist && (git commit -m 'chore: update dist' || exit 0)",
"precommit": "lint-staged",
"prettier:fix": "prettier --write ."
},
"lint-staged": {
Expand Down Expand Up @@ -87,10 +86,11 @@
"dependencies": {},
"repository": {
"type": "git",
"url": "git+https://github.com/kaltura/playkit-js-providers.git"
"url": "git+https://github.com/othermedia/playkit-js-providers.git"
},
"bugs": {
"url": "https://github.com/kaltura/playkit-js-providers/issues"
},
"homepage": "https://github.com/kaltura/playkit-js-providers#readme"
"homepage": "https://github.com/kaltura/playkit-js-providers#readme",
"pre-push": []
}
26 changes: 24 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,31 @@
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="./playkit-streamamg-provider.js" type="text/javascript"></script>
</head>
<body>
<script>
</script>
<script type="text/javascript">
// Step 1 - Create a provider options object
var options = {
partnerId: 3000178,
ks: 'Y2Q1YjgxM2NmNWQ0MDU5ZjVkMjg1YjJiOTZmNjhjMTBmNTQyZTYzM3wzMDAwMTc4OzMwMDAxNzg7MTYxODY3NDAwMDswOzE2MTg2NTYwMDA7Vmlld2VyO3N2aWV3OjBfMXJyMWp5NTYsc2V0cm9sZTpQTEFZQkFDS19CQVNFX1JPTEUsd2lkZ2V0OjE7Ow==',
env: {
cdnUrl: "https://open.http.mp.streamamg.com/",
serviceUrl: "https://open.http.mp.streamamg.com/api_v3"
}
};
// Step 2 - Create a provider instance
var provider = new playkit.providers.streamamg.Provider(options);
// Step 3 - Create media info object
var mediaInfo = {
entryId: '0_1rr1jy56' // Mandatory
//ks: "YOUR_KS" // Optional
};
// Step 4 - Get the media config
provider.getMediaConfig(mediaInfo).then(function(mediaConfig) {
// Manipulate media config
console.log(mediaConfig);
});
</script>
</body>
</html>
26 changes: 26 additions & 0 deletions src/k-provider/streamamg/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//@flow
import {clone} from '../../util/clone';

const defaultConfig: Object = {
serviceUrl: 'https://cdnapisec.kaltura.com/api_v3',
cdnUrl: 'https://cdnapisec.kaltura.com',
serviceParams: {
apiVersion: '3.3.0',
format: 1
},
useApiCaptions: true
};

export default class StreamAMGConfiguration {
static set(clientConfig?: ProviderEnvConfigObject) {
if (clientConfig) {
Object.assign(defaultConfig, clientConfig);
}
}

static get(): Object {
return clone(defaultConfig);
}
}

export {StreamAMGConfiguration};
10 changes: 10 additions & 0 deletions src/k-provider/streamamg/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow
import StreamAMGProvider from './provider';

declare var __VERSION__: string;
declare var __NAME__: string;

const NAME = __NAME__ + '-streamamg';
const VERSION = __VERSION__;

export {StreamAMGProvider as Provider, NAME, VERSION};
17 changes: 17 additions & 0 deletions src/k-provider/streamamg/loaders/data-loader-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @flow
import DataLoaderManager from '../../common/data-loader-manager';
import OVPService from '../services/ovp-service';

/**
* OTTDataLoaderManager is a class that handles the OVP data loading
* @param {string} playerVersion - player version
* @param {string} partnerId - partner id
* @param {string} ks - ks
* @param {ProviderNetworkRetryParameters} [networkRetryConfig] - network retry configuration
*/
export default class OVPDataLoaderManager extends DataLoaderManager {
constructor(playerVersion: string, partnerId: number, ks: string = '', networkRetryConfig: ProviderNetworkRetryParameters) {
super(networkRetryConfig);
this._multiRequest = OVPService.getMultiRequest(playerVersion, ks, partnerId);
}
}
93 changes: 93 additions & 0 deletions src/k-provider/streamamg/loaders/media-entry-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
//@flow
import RequestBuilder from '../../../util/request-builder';
import OVPBaseEntryService from '../../ovp/services/base-entry-service';
import OVPMetadataService from '../../ovp/services/meta-data-service';
import StreamAMGConfiguration from '../config';
import KalturaPlaybackContext from '../../ovp/response-types/kaltura-playback-context';
import KalturaMetadataListResponse from '../../ovp/response-types/kaltura-metadata-list-response';
import KalturaBaseEntryListResponse from '../../ovp/response-types/kaltura-base-entry-list-response';
import StreamAMGBaseEntryService from '../services/base-entry-service';
import type {OVPMediaEntryLoaderResponse} from '../../ovp/loaders/media-entry-loader';

export default class StreamAMGMediaEntryLoader implements ILoader {
_entryId: string;
_referenceId: string;
_requests: Array<RequestBuilder>;
_response: any = {};

static get id(): string {
return 'media';
}

/**
* @constructor
* @param {Object} params loader params
* @boolean {boolean} useExternalCaptions - if we should add captions request to the multirequests.
*/
constructor(params: Object) {
this.requests = this.buildRequests(params);
this._entryId = params.entryId;
this._referenceId = params.referenceId;
}

set requests(requests: Array<RequestBuilder>) {
this._requests = requests;
}

get requests(): Array<RequestBuilder> {
return this._requests;
}

set response(response: any) {
console.log(response);
let mediaEntryResponse: KalturaBaseEntryListResponse = new KalturaBaseEntryListResponse(response[0].data);

// OM START: Not supported by StreamAMG Kaltura yet, simulate it via playManifest
response[1].data.sources = [
{
format: 'applehttp',
deliveryProfileId: '',
// KS is added automatically by the library
url: `https://open.http.mp.streamamg.com/p/3000178/sp/300017800/playManifest/entryId/${mediaEntryResponse.entries[0].id}/format/applehttp/protocol/https/a.m3u8`,
protocols: 'https',
flavorIds: response[1].data.flavorAssets.map(asset => asset.id).join(',')
}
];
// OM END

this._response.entry = mediaEntryResponse.entries[0];
this._response.playBackContextResult = new KalturaPlaybackContext(response[1].data);
this._response.metadataListResult = new KalturaMetadataListResponse(response[2].data);
}

get response(): OVPMediaEntryLoaderResponse {
return this._response;
}

/**
* Builds loader requests
* @function
* @param {Object} params Requests parameters
* @returns {RequestBuilder} The request builder
* @static
*/
buildRequests(params: Object): Array<RequestBuilder> {
const config = StreamAMGConfiguration.get();
const requests: Array<RequestBuilder> = [];
requests.push(OVPBaseEntryService.list(config.serviceUrl, params.ks, params.entryId, params.redirectFromEntryId, params.referenceId));
// Use the entry id from the request result to support loading by referenceId
const serviceEntryId = params.ks === '{1:result:ks}' ? '{2:result:objects:0:id}' : '{1:result:objects:0:id}';
requests.push(StreamAMGBaseEntryService.getContextData(config.serviceUrl, params.ks, serviceEntryId));
requests.push(OVPMetadataService.list(config.serviceUrl, params.ks, serviceEntryId));
return requests;
}

/**
* Loader validation function
* @function
* @returns {boolean} Is valid
*/
isValid(): boolean {
return !!(this._entryId || this._referenceId);
}
}
68 changes: 68 additions & 0 deletions src/k-provider/streamamg/provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//@flow
import getLogger from '../../util/logger';
import StreamAMGMediaEntryLoader from './loaders/media-entry-loader';
import OVPSessionLoader from '../ovp/loaders/session-loader';
import OVPDataLoaderManager from './loaders/data-loader-manager';
import Error from '../../util/error/error';
import OVPProvider from '../ovp/provider';
import StreamAMGConfiguration from './config';

export default class StreamAMGProvider extends OVPProvider {
/**
* @constructor
* @param {ProviderOptionsObject} options - provider options
* @param {string} playerVersion - player version
*/
constructor(options: ProviderOptionsObject, playerVersion: string) {
super(options, playerVersion);
this._logger = getLogger('StreamAMGProvider');
StreamAMGConfiguration.set(options.env);
this._setFilterOptionsConfig(options.filterOptions);
this._networkRetryConfig = Object.assign(this._networkRetryConfig, options.networkRetryParameters);
}

/**
* Gets the backend media config.
* @param {OVPProviderMediaInfoObject} mediaInfo - ovp media info
* @returns {Promise<ProviderMediaConfigObject>} - The provider media config
*/
getMediaConfig(mediaInfo: OVPProviderMediaInfoObject): Promise<ProviderMediaConfigObject> {
if (mediaInfo.ks) {
this.ks = mediaInfo.ks;
this._isAnonymous = false;
}
if (this.widgetId !== this.defaultWidgetId) {
this._isAnonymous = false;
}
this._dataLoader = new OVPDataLoaderManager(this.playerVersion, this.partnerId, this.ks, this._networkRetryConfig);
return new Promise((resolve, reject) => {
const entryId = mediaInfo.entryId;
const referenceId = mediaInfo.referenceId;
if (entryId || referenceId) {
let ks: string = this.ks;
if (!ks) {
ks = '{1:result:ks}';
this._dataLoader.add(OVPSessionLoader, {widgetId: this.widgetId});
}
const redirectFromEntryId = this._getEntryRedirectFilter(mediaInfo);
// OM START: Remove the playbackContext that is not supported and add our customised loader
this._dataLoader.add(StreamAMGMediaEntryLoader, {entryId, ks, redirectFromEntryId, referenceId});
// OM END
return this._dataLoader.fetchData().then(
response => {
try {
resolve(this._parseDataFromResponse(response));
} catch (err) {
reject(err);
}
},
err => {
reject(err);
}
);
} else {
reject(new Error(Error.Severity.CRITICAL, Error.Category.PROVIDER, Error.Code.MISSING_MANDATORY_PARAMS, {message: 'missing entry id'}));
}
});
}
}
30 changes: 30 additions & 0 deletions src/k-provider/streamamg/services/base-entry-service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//@flow
import OVPBaseEntryService from '../../ovp/services/base-entry-service';
import RequestBuilder from '../../../util/request-builder';

const SERVICE_NAME: string = 'baseEntry';

export default class StreamAMGBaseEntryService extends OVPBaseEntryService {
/**
* Creates an instance of RequestBuilder for baseentry.getContextData
* @function getContextData
* @param {string} serviceUrl The service base URL
* @param {string} ks The ks
* @param {serviceEntryId} serviceEntryId The entry id from the request result (to support loading by referenceId)
* @returns {RequestBuilder} The request builder
* @static
*/
static getContextData(serviceUrl: string, ks: string, serviceEntryId: string): RequestBuilder {
const headers: Map<string, string> = new Map();
headers.set('Content-Type', 'application/json');
const request = new RequestBuilder(headers);
request.service = SERVICE_NAME;
request.action = 'getContextData';
request.method = 'POST';
request.url = request.getUrl(serviceUrl);
request.tag = 'baseEntry-getContextData';
const contextDataParams = {objectType: 'KalturaEntryContextDataParams', flavorTags: 'all'};
request.params = {entryId: serviceEntryId, ks: ks, contextDataParams: contextDataParams};
return request;
}
}
Loading

0 comments on commit 0654673

Please sign in to comment.