Introduction
Ce document explique comment suivre l'état des tâches en envoyant des demandes d'état à l' CMS API ou en utilisant Dynamic Ingest API des notifications. Nous fournissons également un exemple d'application de tableau de bord qui automatise le processus
Notez que l'état des tâches d'ingestion n'est disponible que pour les tâches soumises au cours des 7 derniers jours.
Statut de la demande
Vous obtenez l'état des tâches d'ingestion dynamique (ingérer, remplacer ou retranscoder) à l'aide de ces CMS API points de terminaison - notez que ces points de terminaison ne fonctionnent que pour les tâches de livraison dynamique:
Obtenez le statut pour tous les travaux
https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs
La réponse ressemblera à ceci :
[
{
"id": "ac49b1db-e6e1-477f-a2c1-70b9cd3107cb",
"state": "finished",
"account_id": "57838016001",
"video_id": "5636411346001",
"error_code": null,
"error_message": null,
"updated_at": "2017-11-07T13:56:51.505Z",
"started_at": "2017-11-07T13:56:12.510Z",
"priority": "normal",
"submitted_at": "2017-11-07T13:56:12.435Z"
},
{
"id": "10605652-8b6f-4f22-b190-01bd1938677b",
"state": "processing",
"account_id": "57838016001",
"video_id": "5636411346001",
"error_code": null,
"error_message": null,
"updated_at": null,
"started_at": null,
"priority": "low",
"submitted_at": "2017-11-07T14:06:35.000Z"
}
]
Obtenir le statut d'un travail spécifique
https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs/{job_id}
La réponse ressemblera à ceci :
{
"id": "ac49b1db-e6e1-477f-a2c1-70b9cd3107cb",
"state": "finished",
"account_id": "57838016001",
"video_id": "5636411346001",
"error_code": null,
"error_message": null,
"updated_at": "2017-11-07T13:56:51.505Z",
"started_at": "2017-11-07T13:56:12.510Z",
"priority": "normal",
"submitted_at": "2017-11-07T13:56:12.435Z"
}
Les valeurs possibles pour state
sommes:
processing
: traitement, la vidéo n'est pas encore lisiblepublishing
: au moins un rendu lisible a été créé et la vidéo est en cours de préparation pour la lecturepublished
: au moins un rendu est disponible pour la lecturefinished
: au moins un rendu audio/vidéo a été traitéfailed
: le traitement a échoué ; si vous n'arrivez pas à comprendre ce qui s'est mal passé, contactez l'assistance
Recevoir des notifications
Bien que la méthode de statut de la demande décrite ci-dessus fonctionne, si vous attendez un état particulier (published
ou finished
), il est préférable de laisser Brightcove vous avertir lorsque ces événements se produisent plutôt que de devoir continuer à demander le statut jusqu'à ce que vous obteniez la réponse que vous recherchez. Nous allons maintenant voir comment vous pouvez créer une application autour de la gestion des notifications.
Les notifications Dynamic Ingest vous donnent toutes les informations dont vous avez besoin pour savoir quand votre vidéo est prête - il vous suffit de savoir quoi rechercher... et de définir ce que "prêt" signifie pour vos systèmes. Ce diagramme résume le flux de travail :
Notifications d'ingestion dynamique
Le service de notification Dynamic Ingest vous envoie des notifications pour plusieurs types d'événements. Les deux qui sont les plus utiles pour déterminer quand la vidéo est "prête" sont celles qui indiquent que des rendus particuliers ont été créés et celle qui indique que tout le traitement est terminé. Voici des exemples de chacun :
Notification de création de rendu dynamique
Remarque dans cet exemple :
- Les
videoId
la valeur vous permet de savoir à quelle vidéo le rendu est destiné (au cas où plusieurs tâches d'ingestion seraient en cours d'exécution) - Les
entity
la valeur est le type de rendu dynamique créé - si la
status
la valeur est "SUCCESS", le rendu a été créé avec succès
Traitement de la notification de fin
Remarque dans cet exemple :
- Les
videoId
etjobId
les valeurs vous permettent de savoir à quelle vidéo il s'agit (au cas où plusieurs tâches d'ingestion seraient en cours d'exécution) - Si la
status
la valeur est "SUCCESS", la vidéo a été traitée avec succès
Pour recevoir des notifications, vous devez inclure un champ « callbacks » dans vos Dynamic Ingest API demandes, pointant vers une ou plusieurs adresses de rappel :
{
"master": {
"url": "https://s3.amazonaws.com/bucket/mysourcevideo.mp4"
}, "profile": "multi-platform-extended-static",
"callbacks": ["https://host1/path1”, “https://host2/path2”]
}
Exemple de tableau de bord
Cette section explique comment les notifications peuvent être rassemblées pour créer un tableau de bord simple pour l'API Dynamic Ingest. Le gestionnaire des notifications analyse les notifications de la pour Dynamic Ingest API identifier le traitement des notifications complètes. Il ajoute ensuite les notifications vidéo dans un tableau d'objets pour chaque vidéo dans un fichier JSON. Le tableau de bord lui-même est une page HTML qui importe le fichier JSON pour obtenir les données de notification. Il utilise les identifiants pour faire une demande au API CMS pour obtenir les métadonnées de la vidéo.
Voici l'architecture de haut niveau de l'application :
Les parties de l'application
Le gestionnaire de notifications est intégré à PHP - il recherche le traitement des notifications complètes et ajoute l'identifiant vidéo à un tableau dans un fichier JavaScript séparé :
<?php
//POST ne fonctionnera pas pour les données JSON
$problem = "No errors";
essayez {
$json = file_get_contents('php://input');
$decoded = json_decode($json, true);
} capture (Exception $e) {
$problem = $e->getMessage();
echo $problem ;
}
//Notification complète
$notification = json_encode($decoded, JSON_PRETTY_PRINT);
//Commencez par extraire les parties utiles de la notification
//pour la livraison dynamique, recherchez 'VideoID'
if (isset ($decoded ["VideoID"])) {
$videoID = $decoded ["VideoID"] ;
} elseif (isset ($ décodé ["entité"])) {
$videoID = $decoded ["entité"] ;
} else {
$videoID = null ;
}
if (isset($decoded["entityType"])) {
$entityType = $decoded["entityType"];
} else {
$entityType = null ;
}
if (isset ($décoded ["status"])) {
$status = $decoded ["status"] ;
} else {
$status = null ;
}
if (isset ($ décodé ["action"]) {
$action = $décoded ["action"] ;
} else {
$action = null ;
}
//si la notification concerne le titre complété, agissez
if (($entityType == 'TITLE') && ($action == 'CREATE')) {
if (($status == 'SUCCESS') || ($status == 'ÉCHEC')) {
$newLine = « \ nVideoidArray.unshift ( ».$Videoïde.« ) ;
//Dites à PHP où il peut trouver le fichier journal et dire à PHP de l'ouvrir
//et ajoutez la chaîne que nous avons créée plus tôt.
$logFileLocation = "video-ids.js « ;
$fileHandle = fopen($logFileLocation, 'a') or die("-1");
chmod ($LogFileLocation, 0777) ;
fwrite ($FileHandle, $newLine) ;
fclose ($FileHandle) ;
}
}
//enregistrer la notification complète pour la piste d'audit
$logEntry = $notification. », \ n » ;
$logFileLocation = "full-log.txt « ;
$fileHandle = fopen($logFileLocation, 'a') or die("-1");
chmod ($LogFileLocation, 0777) ;
fwrite ($FileHandle, $LogEntry) ;
fclose ($FileHandle) ;
echo « L'application de rappel Dynamic Ingest est en cours d'exécution » ;
?>
Fichier JSON :
Le fichier JSON est initialement un tableau vide ( []
) - les données sont ajoutées par le gestionnaire de notification.
Tableau de bord
Le tableau de bord inclut le code HTML et JavaScript pour récupérer les données de notification et les données vidéo supplémentaires à partir du CMS API et écrire les résultats dans un tableau :
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Dynamic Ingest Log</title>
<style>
body {
font-family: sans-serif;
margin: 5em;
}
.hide {
display: none;
}
.show {
display: block;
}
table {
border-collapse: collapse;
border: 1px #999999 solid;
}
th {
background-color: #666666;
color: #f5f5f5;
padding: .5em;
font-size: .7em;
}
td {
border: 1px #999999 solid;
font-size: .7em;
padding: .5em
}
.hidden {
display: none;
}
</style>
</head>
<body>
<h1>Dynamic Ingest Log</h1>
<h2>Account: Brightcove Learning (57838016001)</h2>
<p style="width:70%">
Videos are listed in order of processing completion time, newest to oldest. The reference id (generated by the <a href="./di-tester.html">Dynamic Ingest tester</a>) is a combination of the date/time that the Dynamic Ingest job was initiated and the ingest profile that was used. You can add additional videos using the <a href="./di-tester.html">Dynamic Ingest tester</a>. New videos will appear in this log after processing is complete.
</p>
<p>
<button id="clearLogBtn">Clear the log</button>
</p>
<div id="videoLogBlock">
<table>
<thead>
<tr>
<th>Video ID</th>
<th>Name</th>
<th>Reference ID</th>
<th>Renditions Created</th>
<th>Processing Complete</th>
</tr>
</thead>
<tbody id="logBody"></tbody>
</table>
<h4 id="loadingMessage">Loading data, please wait...</h4>
</div>
<script>
var BCLS = ( function (window, document) {
// to use another account, set the account_id value appropriately
// the client_id and client_secret will also need to be changed in the proxy
var my_account_id = 57838016001,
account_id = my_account_id,
logBody = document.getElementById('logBody'),
loadingMessage = document.getElementById('loadingMessage'),
clearLogBtn = document.getElementById('clearLogBtn'),
i = 0,
iMax,
// set the proxyURL to the location of the proxy app that makes Brightcove API requests
proxyURL = './brightcove-learning-proxy.php',
dataFileURL = './di.json',
videoDataArray = [],
requestOptions = {},
currentVideo,
currentIndex = 0;
/**
* tests for all the ways a variable might be undefined or not have a value
* @param {*} x the variable to test
* @return {Boolean} true if variable is defined and has a value
*/
function isDefined(x) {
if ( x === '' || x === null || x === undefined || x === NaN) {
return false;
}
return true;
}
/**
* find index of an object in array of objects
* based on some property value
*
* @param {array} targetArray - array to search
* @param {string} objProperty - object property to search
* @param {string|number} value - value of the property to search for
* @return {integer} index of first instance if found, otherwise returns null
*/
function findObjectInArray(targetArray, objProperty, value) {
var i, totalItems = targetArray.length, objFound = false;
for (i = 0; i < totalItems; i++) {
if (targetArray[i][objProperty] === value) {
objFound = true;
return i;
}
}
if (objFound === false) {
return null;
}
}
/**
* factory for new video objects
* @param {String} videoId the video id
* @return {object} the new object
*/
function makeVideoDataObject(videoId) {
var obj = {};
obj.id = videoId;
obj.name = '';
obj.reference_id = '';
obj.renditions = 0;
obj.complete = 'no';
return obj;
}
/**
* processes notification objects
* creates a new object in the videoDataArray if it doesn't exist
* and updates the videoDataArray object based on the notification
* @param {Object} notificationObj the raw notification object
*/
function processNotification(notificationObj) {
var objIndex, videoObj;
// if notification object contains a video id, find the corresponding
// object in the videoDataArray or create it if it's not there
if (isDefined(notificationObj) && isDefined(notificationObj.videoId)) {
objIndex = findObjectInArray(videoDataArray, 'id', notificationObj.videoId);
// if not found, create one
if (!isDefined(objIndex)) {
videoObj = makeVideoDataObject(notificationObj.videoId);
videoDataArray.push(videoObj);
objIndex = videoDataArray.length - 1;
}
// now update properties based on what's in the notification
if (notificationObj.entityType === 'DYNAMIC_RENDITION') {
// increment the renditions account
videoDataArray[objIndex].renditions++;
}
} else if (notificationObj.entityType === 'TITLE') {
// overall processing notification - checked for SUCCESS / FAILED
if (notificationObj.status === 'SUCCESS') {
// mark complete
videoDataArray[objIndex].complete = 'yes';
} else if (notificationObj.status === 'FAILED') {
// mark failed
videoDataArray[objIndex].complete = 'failed';
}
}
return;
}
/**
* creates the dashboard table body
*/
function writeReport() {
var j,
jMax = videoDataArray.length,
item,
t;
loadingMessage.textContent = 'This page will refresh in 1 minute...';
for (j = 0; j < jMax; j++) {
item = videoDataArray[j];
if (item.id !== undefined) {
logBody.innerHTML += '<tr><td>' + item.id + '</td><td>' + item.name + '</td><td>' + item.reference_id + '</td><td>' + item.renditions + '</td><td>' + item.complete + '</td></tr>';
}
}
// set timeout for refresh
t = window.setTimeout(init, 60000);
};
// function to set up the notification data request
function setJSONRequestOptions() {
submitRequest(null, dataFileURL, 'notificationData');
}
// function to set up video data request
function setVideoRequestOptions() {
requestOptions = {};
requestOptions.url = 'https://cms.api.brightcove.com/v1/accounts/' + account_id + '/videos/' + currentVideo.id;
submitRequest(requestOptions, proxyURL, 'video');
}
/**
* initiates the CMS API requests
*/
function getVideoInfo() {
iMax = videoDataArray.length;
if (currentIndex < iMax) {
currentVideo = videoDataArray[currentIndex];
setVideoRequestOptions();
} else {
loadingMessage.innerHTML = 'No videos have been ingested - you can add some using the <a href="./di-tester.html">Dynamic Ingest tester</a>';
}
}
/**
* make the CMS API requests
* @param {Object} options request options
* @param (String) url URL to send request to
* @param (String) type the request type
*/
function submitRequest(options, url, type) {
var httpRequest = new XMLHttpRequest(),
requestData,
responseData,
videoDataObject,
parsedData,
getResponse = function () {
try {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
responseData = httpRequest.responseText;
switch (type) {
case 'notificationData':
var k, kMax, dataArray;
dataArray = JSON.parse(responseData);
// process the notifications
kMax = dataArray.length;
for (k = 0; k < kMax; k++) {
processNotification(dataArray[k]);
}
getVideoInfo();
break;
case 'video':
parsedData = JSON.parse(responseData);
videoDataArray[currentIndex].reference_id = parsedData.reference_id;
videoDataArray[currentIndex].name = parsedData.name;
currentIndex++;
if (currentIndex < iMax) {
currentVideo = videoDataArray[currentIndex];
setVideoRequestOptions();
} else {
writeReport();
}
break;
}
} else {
console.log('There was a problem with the request. Request returned '', httpRequest.status);
if (type === 'video') {
setVideoRequestOptions();
} else {
setSourcesRequestOptions();
}
}
}
}
catch(e) {
console.log('Caught Exception: ', e);
}
};
// notifications data is a special case
if (type === 'notificationData') {
// set response handler
httpRequest.onreadystatechange = getResponse;
// open the request
httpRequest.open("GET", url);
// set headers
httpRequest.setRequestHeader("Content-Type", "application/json");
// open and send request
httpRequest.send();
} else {
// requests via proxy
// set up request data
requestData = "url=" + encodeURIComponent(options.url) + "&requestType=GET";
// set response handler
httpRequest.onreadystatechange = getResponse;
// open the request
httpRequest.open("POST", url);
// set headers
httpRequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// open and send request
httpRequest.send(requestData);
}
};
// event handlers
clearLogBtn.addEventListener('click', function () {
if (window.confirm('Are you sure? This action cannot be undone!')) {
// if your clear-log app resides in another location, change the URL
window.location.href = 'clear-log.php';
}
});
// get things started
function init() {
// clear table and the video data array
logBody.innerHTML = "";
videoDataArray = [];
setJSONRequestOptions();
}
// kick off the app
init();
})(window, document);
</script>
</body>
</html>
Procuration
<?php
/**
* brightcove-learning-proxy.php - proxy pour les API RESTful Brightcove
* obtient un jeton d'accès, fait la requête et renvoie la réponse
* Accès :
* URL : https://solutions.brightcove.com/bcls/bcls-proxy/bcsl-proxy.php
* (notez que vous devriez *toujours* accéder au proxy via HTTPS)
* Méthode : POST
*
* @post {string} url - l'URL de la requête API
* @post {string} [requestType=get] - méthode HTTP pour la requête
* @post {string} [requestBody=null] - Données JSON à envoyer avec des requêtes d'écriture
*
* @returns {string} $response - Réponse JSON reçue de l'API
*/
//Entablement CORS
en-tête (« Access-Control-Allow-Origin :* ») ;
//configuration de la demande de jeton d'accès
$data = array();
//
//changer les valeurs ci-dessous pour utiliser ce proxy avec un autre compte
//
$client_id = "YOUR_CLIENT_ID_HERE";
$client_secret = "YOUR_CLIENT_SECRET_HERE";
$auth_string = "{$client_id}:{$client_secret}";
$request = "https://oauth.brightcove.com/v4/access_token?grant_type=client_credentials";
$ch = curl_init($request);
curl_setopt_array($ch, array(
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_USERPWD => $auth_string,
CURLOPT_HTTPHEADER => array(
'Content-type: application/x-www-form-urlencoded',
),
CURLOPT_POSTFIELDS => $data
))) ;
$response = curl_exec($ch);
curl_close ($ch) ;
//Vérification des erreurs
if ($response === FALSE) {
die (curl_error ($ch)) ;
}
//Décode la réponse
$responseData = json_decode($response, TRUE);
$access_token = $responseData["access_token"];
//configuration de l'appel d'API
//Obtenir des données
if ($_POST["requestBody"]) {
$data = json_decode($_POST["requestBody"]);
} else {
$data = array();
}
//Obtenir le type de requête ou par défaut à GET
if ($_POST["requestType"]) {
$method = $_POST["requestType"];
} else {
$method = "GET";
}
//récupère l'URL et les informations d'autorisation à partir des données du formulaire
$request = $_POST["url"];
//envoyer la requête http
$ch = curl_init($request);
curl_setopt_array($ch, array(
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_HTTPHEADER => array(
'Content-type : application/json',
« Autorisation : Porteur {$access_token} »,
),
CURLOPT_POSTFIELDS => json_encode($data)
))) ;
$response = curl_exec($ch);
curl_close ($ch) ;
//Vérification des erreurs
if ($response === FALSE) {
echo « Erreur : « +$réponse ;
die (curl_error ($ch)) ;
}
//Décode la réponse
//$responseData = json_decode ($response, TRUE) ;
//retourne la réponse à l'appelant AJAX
echo $ réponse ;
?>
Effacer le journal
Cette application PHP simple restaure simplement le fichier JavaScript à son état d'origine, en effaçant les anciens identifiants vidéo :
<?php
$logFileLocation = "di.json";
$freshContent = array ();
$encodedContent = json_encode($freshContent);
file_put_contents ($logFileLocation, $encodedContent) ;
echo 'Log file cleared - <a href="di-log.html">go back to the dashboard</a>';
?>