Introducción
Este documento explica cómo puede realizar un seguimiento del estado de los trabajos realizando solicitudes de estado al CMS API o usando Dynamic Ingest API notificaciones. También proporcionamos una aplicación de panel de control de muestra que automatiza el proceso
Tenga en cuenta que el estado de los trabajos de ingesta solo está disponible para los trabajos enviados en los últimos 7 días.
Solicitando estado
Obtiene el estado de los trabajos de ingesta dinámica (ingesta, reemplaza o retranscodifica) usando estas CMS API puntos finales - tenga en cuenta que estos puntos finales funcionan para Solo trabajos de entrega dinámica:
Obtener el estado de todos los trabajos
https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs
La respuesta se verá así:
[
{
"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"
}
]
Obtener el estado de trabajos específicos
https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs/{job_id}
La respuesta se verá así:
{
"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"
}
Los posibles valores para state
están:
processing
: procesando, el video aún no se puede reproducirpublishing
: se ha creado al menos una reproducción reproducible y el video se está preparando para su reproducciónpublished
: al menos una interpretación está disponible para su reproducciónfinished
: se ha procesado al menos una reproducción de audio / videofailed
: procesamiento fallido; Si no puede averiguar qué salió mal, comuníquese con Soporte
Recibir notificaciones
Si bien el método de estado de solicitud mencionado anteriormente funciona, si está esperando un estado en particular (published
o finished
), es mejor dejar que Brightcove le notifique cuando ocurran estos eventos en lugar de tener que seguir preguntando por el estado hasta que obtenga la respuesta que está buscando. Ahora veremos cómo puede crear una aplicación en torno al manejo de notificaciones.
Las notificaciones de ingesta dinámica le brindan toda la información que necesita saber cuando su video está listo; solo necesita saber qué buscar... y definir qué significa "listo" para sus sistemas. Este diagrama resume el flujo de trabajo:
Notificaciones de ingesta dinámica
El servicio de notificación Dynamic Ingest le envía notificaciones para varios tipos de eventos. Los dos que son más útiles para determinar cuándo el video está "listo" son los que indican que se han creado representaciones particulares y el que indica que todo el procesamiento está completo. Aquí hay ejemplos de cada uno:
Notificación de creación de reproducción dinámica
Tenga en cuenta en este ejemplo:
- La
videoId
El valor le permite saber para qué video es la reproducción (en caso de que tenga varios trabajos de ingesta en ejecución) - La
entity
valor es el tipo de reproducción dinámica creado - Si el
status
el valor es "ÉXITO", la representación se creó correctamente
Procesando notificación completa
Tenga en cuenta en este ejemplo:
- La
videoId
yjobId
los valores le permiten saber para qué video es esto (en caso de que tenga varios trabajos de ingesta en ejecución) - Si el
status
El valor es "SUCCESS", el video se procesó correctamente.
Para recibir notificaciones, debe incluir un campo "devoluciones de llamada" en usted Dynamic Ingest API solicitudes, apuntando a una o más direcciones de devolución de llamada:
{
"master": {
"url": "https://s3.amazonaws.com/bucket/mysourcevideo.mp4"
}, "profile": "multi-platform-extended-static",
"callbacks": ["https://host1/path1”, “https://host2/path2”]
}
Panel de control de muestra
Esta sección explica cómo se pueden juntar las notificaciones para construir un tablero simple para la API de Dynamic Ingest. El controlador de notificaciones analiza las notificaciones del Dynamic Ingest API para identificar el procesamiento de notificaciones completas. Luego agrega las notificaciones de video en una matriz de objetos para cada video en un archivo JSON. El tablero en sí es una página HTML que importa el archivo JSON para obtener los datos de notificación. Utiliza los identificadores para realizar una solicitud al API de CMS para obtener los metadatos del video.
Aquí está la arquitectura de alto nivel de la aplicación:
Las partes de la aplicación
El controlador de notificaciones está integrado en PHP: busca procesar notificaciones completas y agrega la identificación del video a una matriz en un archivo JavaScript separado:
<?php
// POST no funcionará para datos JSON
$problem = "No errors";
intentar {
$json = file_get_contents('php://input');
$decoded = json_decode($json, true);
} catch (Exception $e) {
$problem = $e->getMessage();
echo $ problema;
}
// notificación completa
$notification = json_encode($decoded, JSON_PRETTY_PRINT);
// Empiece por extraer las partes útiles de la notificación
// para Dynamic Delivery, busque 'videoId'
if (isset ($ decoded ["videoId"])) {
$videoId = $decoded["videoId"];
} elseif (isset($decoded["entity"])) {
$videoId = $decoded["entity"];
} else {
$videoId = null;
}
if (isset ($ decoded ["entityType"])) {
$entityType = $decoded["entityType"];
} else {
$entityType = null;
}
if (isset ($ decoded ["estado"])) {
$status = $decoded["status"];
} else {
$status = null;
}
if (isset ($ decoded ["acción"])) {
$action = $decoded["action"];
} else {
$action = null;
}
// si la notificación es por título completo, actuar
if (($ entityType == 'TITLE') && ($ action == 'CREATE')) {
if (($ status == 'SUCCESS') || ($ status == 'FAILED')) {
$newLine = "\nvideoIdArray.unshift(".$videoId.");";
// Dígale a PHP dónde puede encontrar el archivo de registro y dígale a PHP que lo abra
// y agregue la cadena que creamos anteriormente.
$logFileLocation = "video-ids.js";
$fileHandle = fopen($logFileLocation, 'a') or die("-1");
chmod ($ logFileLocation, 0777);
fwrite ($ fileHandle, $ newLine);
fclose ($ fileHandle);
}
}
// guardar la notificación completa para la pista de auditoría
$logEntry = $notificación.",\\n";
$logFileLocation = "full-log.txt";
$fileHandle = fopen($logFileLocation, 'a') or die("-1");
chmod ($ logFileLocation, 0777);
fwrite ($ fileHandle, $ logEntry);
fclose ($ fileHandle);
echo "La aplicación de devolución de llamada Dynamic Ingest se está ejecutando";
?>
Archivo JSON:
El archivo JSON es inicialmente una matriz vacía ( []
) - los datos son agregados por el controlador de notificaciones.
Salpicadero
El tablero incluye HTML y JavaScript para obtener los datos de notificación y datos de video adicionales del CMS API y escribe los resultados en una tabla:
<!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>
Apoderado
<? php
/ **
* brightcove-learning-proxy.php - proxy para las API RESTful de Brightcove
* obtiene un token de acceso, realiza la solicitud y devuelve la respuesta
* Accediendo:
* URL: https://solutions.brightcove.com/bcls/bcls-proxy/bcsl-proxy.php
* (tenga en cuenta que debe * siempre * acceder al proxy a través de HTTPS)
* Método: PUBLICACIÓN
*
* @post {string} url: la URL de la solicitud de API
* @post {string} [requestType = GET]: método HTTP para la solicitud
* @post {string} [requestBody = null]: datos JSON que se enviarán con solicitudes de escritura
*
* @returns {string} $ response: respuesta JSON recibida de la API
* /
// entablement CORS
encabezado ("Acceso-Control-Permitir-Origen: *");
// configurar la solicitud de token de acceso
$data = array();
//
// cambia los valores a continuación para usar este proxy con una cuenta diferente
//
$client_id = "TU_ID_CLIENTE_AQUÍ";
$client_secret = "YOUR_CLIENT_SECRET_HERE";
$auth_string = "{$client_id}:{$client_secret}";
$solicitud = "https://oauth.brightcove.com/v4/access_token?grant_type=client_credentials";
$ch = curl_init($solicitud);
curl_setopt_array ($ ch, array (
CURLOPT_POST => VERDADERO,
CURLOPT_RETURNTRANSFER => VERDADERO,
CURLOPT_SSL_VERIFYPEER => FALSO,
CURLOPT_USERPWD => $ auth_string,
CURLOPT_HTTPHEADER => matriz (
'Tipo de contenido: aplicación / x-www-form-urlencoded',
),
CURLOPT_POSTFIELDS => $ datos
));
$response = curl_exec($ch);
curl_close ($ ch);
// Verifica si hay errores
if ($ respuesta === FALSO) {
morir (curl_error ($ ch));
}
// Decodifica la respuesta
$responseData = json_decode($response, TRUE);
$access_token = $responseData["access_token"];
// configura la llamada a la API
// obtener datos
if ($ _POST ["requestBody"]) {
$data = json_decode($_POST["requestBody"]);
} else {
$data = array();
}
// obtener el tipo de solicitud o por defecto GET
if ($ _POST ["requestType"]) {
$method = $_POST["requestType"];
} else {
$method = "GET";
}
// obtener la URL y la información de autorización de los datos del formulario
$request = $_POST["url"];
// envía la solicitud http
$ch = curl_init($request);
curl_setopt_array ($ ch, array (
CURLOPT_CUSTOMREQUEST => $ método,
CURLOPT_RETURNTRANSFER => VERDADERO,
CURLOPT_SSL_VERIFYPEER => FALSO,
CURLOPT_HTTPHEADER => matriz (
'Tipo de contenido: aplicación / json',
"Autorización: Portador {$ access_token} ",
),
CURLOPT_POSTFIELDS => json_encode ($ datos)
));
$response = curl_exec($ch);
curl_close ($ ch);
// Verifica si hay errores
if ($ respuesta === FALSO) {
echo "Error:" + $ respuesta;
morir (curl_error ($ ch));
}
// Decodifica la respuesta
// $responseData = json_decode($response, TRUE);
// devuelve la respuesta a la persona que llama AJAX
echo $ respuesta;
?>
Limpiar el registro
Esta sencilla aplicación PHP simplemente restaura el archivo JavaScript a su estado original, borrando los viejos identificadores de video:
<? php
$logFileLocation = "di.json";
$freshContent = array ();
$encodedContent = json_encode($freshContent);
file_put_contents ($ logFileLocation, $ encodedContent);
echo 'Archivo de registro borrado - <a href="di-log.html"> volver al panel </a>';
?>