Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Video Indexer v2 #66

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
2 changes: 2 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export * from './vision/emotion';
export * from './vision/face';
export * from './vision/video';

export * from './vision/videoIndexerV2'

export interface ContentTypeHeaders {
/**
* Media type of the body sent to the API.
Expand Down
3 changes: 2 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ module.exports = {
bingSpeech: require('./speech/bingSpeech'),
bingEntitySearch: require('./search/bingEntitySearch'),
contentModerator: require('./vision/contentModerator'),
videoIndexer: require('./vision/videoIndexer')
videoIndexer: require('./vision/videoIndexer'),
videoIndexerV2: require('./vision/videoIndexerV2')
};
33 changes: 33 additions & 0 deletions src/vision/videoIndexerV2.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { CommonConstructorOptions, ContentTypeHeaders, OcpApimSubscriptionKeyHeaders, ContentTypeHeaderTypes } from "../index";

/**
* Video Indexer is a cloud service that enables you to extract the following insights from your videos using artificial intelligence technologies:
*/
export class videoIndexer {

constructor(options: videoIndexerV2Options);
getAccounts(options: GetAccountOptions): Promise<Object[]>;
getAccountAccessToken(options: GetAccountAccessTokenOptions): Promise<string>
getUserAccessToken(options: GetUserAccessTokenOptions): Promise<string>
}

export interface videoIndexerV2Options {
apiKey: string
}

export interface GetAccountOptions {
location: string
generateAccessTokens?: boolean,
allowEdit?: boolean,
}

export interface GetAccountAccessTokenOptions {
location: string,
accountId: string,
allowEdit?: boolean
}

export interface GetUserAccessTokenOptions {
location: string,
allowEdit?: boolean
}
319 changes: 319 additions & 0 deletions src/vision/videoIndexerV2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,319 @@
const commonService = require('../commonService');
const querystring = require('querystring');

/**
* Video Indexer is a cloud service that enables you to extract the following insights from your videos using artificial intelligence technologies:
*/
class videoIndexerV2 extends commonService {
/**
* Constructor.
*
* @param {Object} obj
* @param {string} obj.apiKey
*/
constructor({ apiKey }) {
const endpoint = "api.videoindexer.ai";
super({ apiKey, endpoint });
this.endpoints = [
endpoint
];
// https://docs.microsoft.com/en-us/azure/media-services/media-services-media-encoder-standard-formats
this.supportedFormats = ['flv', 'mxf', 'gxf', 'ts', 'ps', '3gp', '3gpp', 'mpg', 'wmv', 'asf', 'avi', 'mp4', 'm4a', 'm4v', 'isma', 'ismv', 'dvr-ms', 'mkv', 'wav', 'mov']
}

/**
* Get Accounts - returns the account details associated
* with an API key.
* @param {Object} obj
* @param {string} obj.location
* @param {boolean} obj.generateAccessTokens
* @param {boolean} obj.allowEdit
* @returns {Promise<[Object]>} A promise containing an array of objects
*/
getAccounts({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this specific to the video indexer api? If not, can we pull this out into a separate file?

location,
generateAccessTokens,
allowEdit,
}) {
const operation = {
parameters: [
{
name: 'location',
required: true,
type: 'routeParam',
typeName: 'string'
},
{
name: 'generateAccessTokens',
required: false,
type: 'queryStringParam',
typeName: 'boolean',
},{
name: 'allowEdit',
required: false,
type: 'queryStringParam',
typeName: 'boolean'
},
],
path: `auth/{location}/Accounts`,
method: 'GET'
};

const requestParameters = {
location,
generateAccessTokens,
allowEdit
}

return this.makeRequest({
operation: operation,
parameters: requestParameters
})
}

/**
* Get Account Access Token - returns an account access token as a string.
* @param {Object} obj
* @param {string} obj.location
* @param {boolean} obj.accountId
* @param {boolean} obj.allowEdit
* @returns {Promise<string>} A promise containing an access token
*/
getAccountAccessToken({
location,
accountId,
allowEdit,
}){
const operation = {
parameters: [
{
name: 'location',
required: true,
type: 'routeParam',
typeName: 'string'
},
{
name: 'accountId',
required: true,
type: 'routeParam',
typeName: 'string',
},{
name: 'allowEdit',
required: false,
type: 'queryStringParam',
typeName: 'boolean'
},
],
path: `auth/{location}/Accounts/{accountId}/AccessToken`,
method: 'GET'
};

const requestParameters = {
location,
accountId,
allowEdit
}

return this.makeRequest({
operation: operation,
parameters: requestParameters
})
}

/**
* Get User Access Token - returns a user access token as a string.
* @param {Object} obj
* @param {string} obj.location
* @param {boolean} obj.allowEdit
* @returns {Promise<string>} A promise containing an access token
*/
getUserAccessToken({
location,
allowEdit,
}){
const operation = {
parameters: [
{
name: 'location',
required: true,
type: 'routeParam',
typeName: 'string'
},{
name: 'allowEdit',
required: false,
type: 'queryStringParam',
typeName: 'boolean'
},
],
path: `auth/{location}/Users/Me/AccessToken`,
method: 'GET'
};

const requestParameters = {
location,
allowEdit
}

return this.makeRequest({
operation: operation,
parameters: requestParameters
})
}

/**
* Uploads a video to the Video Indexer
*/
uploadVideo(params){
if(!params.path && !params.videoUrl) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if I specify both? Which will be used?

throw new Error('no video to upload. A video must be specified, either via videoUrl or a path');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this covered by tests?

}

const pathToVideo = (params.path) ? params.path : params.videoUrl;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified via params.path || params.videoUrl?


const isFormatSupported = this.supportedFormats.filter(sf => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this covered by tests?

return pathToVideo.endsWith(sf);
}).length
if (isFormatSupported == 0) {
return Promise.reject(reject(new Error('Unsupported format. Supported formats are ' + this.supportedFormats.join(','))));
}

const requestHeaders = {};

if(params.videoUrl && !((/(http)s?(:\/\/)/).test(params.videoUrl))){
Copy link
Collaborator

@miparnisari miparnisari Jun 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line and line 190 can be simplified: if(<regex>.test(pathToVideo)). No need to do this check twice

// video should point to public url
throw new Error('videoUrl is not an http/https link. A videoUrl must contain a public path');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this covered by tests?

} else if(params.videoUrl) {
// video needs to be url encoded
params.videoUrl = querystring.stringify(params.videoUrl);
}

if(params.path && ((/(http)s?(:\/\/)/).test(params.path))){
// path should not point to public url
throw new Error('path is an http/https link. A path must point to a local path');
} else if(params.path) {
// set content type as video will be sent in request body
requestHeaders['Content-Type'] = 'multipart/form-data';
}

const operation = {
parameters: [
{
name: 'location',
required: true,
type: 'routeParam',
typeName: 'string'
},{
name: 'accountId',
required: true,
type: 'routeParam',
typeName: 'string',
},{
name: 'accessToken',
required: true,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'name',
required: true,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'indexingPreset',
required: false,
type: 'queryStringParam',
typeName: 'string',
options: ['Default',"AudioOnly","DefaultWithNoiseReduction"]
},{
name: 'description',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'partition',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'externalId',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'callbackUrl',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'metadata',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'language',
required: false,
type: 'queryStringParam',
typeName: 'string',
options: ['ar-EG',"zh-Hans","en-US","fr-FR", "de-DE", "it-IT", "ja-JP", "pt-BR", "ru-RU", "es-ES", "auto"]
},{
name: 'videoUrl',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'fileName',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'streamingPreset',
required: false,
type: 'queryStringParam',
typeName: 'string',
options: ['Default',"SingleBitrate","AdaptiveBitrate","NoStreaming"]
},{
name: 'linguisticModelId',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'privacy',
required: false,
type: 'queryStringParam',
typeName: 'string',
options: ['Private','Public']
},{
name: 'externalUrl',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'assetId',
required: false,
type: 'queryStringParam',
typeName: 'string'
},{
name: 'priority',
required: false,
type: 'queryStringParam',
typeName: 'string',
options: ['Low','Normal','High']
},{
"name": "path",
"description": "The path to the file",
"required": false,
"typeName": "string"
}
],
path: '{location}/Accounts/{accountId}/Videos',
method: 'POST'
};

return this.makeRequest({
operation: operation,
parameters: params,
headers: requestHeaders
});
}
}

module.exports = videoIndexerV2;
4 changes: 4 additions & 0 deletions test/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,9 @@ module.exports = {
apiKey: "insert-key-here",
appID: "insert-appID-here",
versionID: "0.1"
},
videoIndexerV2: {
apiKey: "insert-key-here",
accountId: "insert-accountId-here"
}
}
Loading