soporte Contactar con asistencia técnica | estado del sistema Estado del Sistema

Obtener el estado de solicitudes dinámicas de ingesta

Cuando se utiliza el Dynamic Ingest API para agregar videos a tu Video Cloud cuenta, lo que más quieres saber es cuándo se procesó el video y si las reproducciones se crearon con éxito o no.

Introducción

Este documento explica cómo puede realizar un seguimiento del estado de los trabajos realizando una solicitud de estado al CMS API o usando Dynamic Ingest API notificaciones. También ofrecemos una aplicación de tablero de muestra que automatiza el proceso

Tenga en cuenta que el estado de los trabajos de ingesta está disponible solo para los trabajos enviados con los últimos días de 7.

Estado de solicitud

Obtiene el estado de los trabajos de ingesta dinámica (ingesta, reemplazo o cambio de código) usando estas CMS API criterios de valoración - Tenga en cuenta que estos puntos finales funciona para Solo trabajos de entrega dinámica:

Obtener estado para todos los trabajos

    https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs

La respuesta se verá algo 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 estatus para trabajos específicos

    https://cms.api.brightcove.com/v1/accounts/{account_id}/videos/{video_id}/ingest_jobs/{job_id}

La respuesta se verá algo 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 valores posibles para state son:

  • processing: procesamiento, el video aún no se puede reproducir
  • publishing: se ha creado al menos una reproducción reproducible y el video se está preparando para su reproducción
  • published: al menos una reproducción está disponible para reproducción
  • finished: todo el procesamiento es completo
  • failed: procesamiento fallido; Si no puede averiguar qué salió mal, contacte a Soporte

Recibiendo notificaciones

Si bien el método de estado de solicitud descrito anteriormente funciona, si está esperando un estado en particular (published or finished), es mejor dejar que Brightcove le notifique cuando ocurran estos eventos en lugar de tener que seguir preguntando el estado hasta que obtenga la respuesta que busca. Ahora veremos cómo puede crear una aplicación para manejar las notificaciones.

Las notificaciones de Ingreso dinámico 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:

Flujo de trabajo de estado de ingreso
Flujo de trabajo de estado de ingreso

Notificaciones de ingesta dinámica

El servicio de notificación de Ingreso dinámico 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 las representaciones particulares y la que indica que todo el procesamiento está completo. Aquí hay ejemplos de cada uno:

Notificación creada dinámica de representación

    {
      "entity": "default/video3800",
      "entityType": "DYNAMIC_RENDITION",
      "version": "1",
      "action": "CREATE",
      "jobId": "d3ef8751-2b88-4141-95d5-83f0393aca07",
      "videoId": "5660367449001",
      "dynamicRenditionId": "default\/video3800",
      "bitrate": 3804,
      "width": 1920,
      "height": 1080,
      "accountId": "57838016001",
      "status": "SUCCESS"
    }
    
    
Notification for Rendition Created

Tenga en cuenta en este ejemplo:

  • La videoId valor le permite saber para qué video es la versión (en caso de que tenga varios trabajos de ingesta en ejecución)
  • La entity valor es el tipo de representación dinámica creado
  • si el status valor es "SUCCESS", la versión fue creada con éxito

Procesando la notificación completa

    {
      "entity": "5660367449001",
      "entityType": "TITLE",
      "version": "1",
      "action": "CREATE",
      "jobId": "d3ef8751-2b88-4141-95d5-83f0393aca07",
      "videoId": "5660367449001",
      "accountId": "57838016001",
      "status": "SUCCESS"
    }
    
    
Notificación de procesamiento completo

Tenga en cuenta en este ejemplo:

  • La videoId y jobId Los valores le permiten saber para qué video es esto (en caso de que tenga varios trabajos de ingesta en ejecución)
  • Si la línea status el valor es "SUCCESS", el video fue procesado con éxito

Para recibir notificaciones, debe incluir un campo de "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": ["http://host1/path1”, “http://host2/path2”]
    }
    
    

Tablero de muestra

Esta sección explica cómo se pueden juntar las notificaciones para crear un panel simple para el Dynamic Ingest API. El manejador de notificaciones analiza las notificaciones de la 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 CMS API para obtener los metadatos del video.

Aquí está la arquitectura de alto nivel de la aplicación:

Ingesta de arquitectura Dashboad
Ingesta de arquitectura Dashboad

Las partes de la aplicación

El controlador para notificaciones está integrado en PHP: busca procesar las notificaciones completas y agrega el ID de video a una matriz en un archivo JavaScript separado:

    <?php
      // POST won't work for JSON data
      $problem = "No errors";
      try {
        $json    = file_get_contents('php://input');
        $decoded = json_decode($json, true);
      } catch (Exception $e) {
        $problem = $e->getMessage();
        echo $problem;
      }
    
      // full notification
      $notification = json_encode($decoded, JSON_PRETTY_PRINT);
    
      // Begin by extracting the useful parts of the notification
      // for Dynamic Delivery, look for 'videoId'
      // for the legacy ingest system, the video id is the 'entity'
    
      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["status"])) {
        $status = $decoded["status"];
      } else {
        $status = null;
      }
    
      if (isset($decoded["action"])) {
        $action = $decoded["action"];
      } else {
        $action = null;
      }
    
      // if notification is for completed title, act
    
      if (($entityType == 'TITLE') && ($action == 'CREATE')) {
        if (($status == 'SUCCESS') || ($status == 'FAILED')) {
          $newLine = "\nvideoIdArray.unshift(".$videoId.");";
          // Tell PHP where it can find the log file and tell PHP to open it
          // and add the string we created earlier to it.
          $logFileLocation = "video-ids.js";
          $fileHandle      = fopen($logFileLocation, 'a') or die("-1");
          chmod($logFileLocation, 0777);
          fwrite($fileHandle, $newLine);
          fclose($fileHandle);
        }
      }
    
      // save full notification for audit trail
      $logEntry = $notification.",\n";
    
      $logFileLocation = "full-log.txt";
      $fileHandle      = fopen($logFileLocation, 'a') or die("-1");
      chmod($logFileLocation, 0777);
      fwrite($fileHandle, $logEntry);
      fclose($fileHandle);
    
    
      echo "Dynamic Ingest callback app is running";
      ?>
      
      

Archivo JSON:

El archivo JSON es inicialmente una matriz vacía ( [] ): el manejador de notificaciones agrega los datos.

Sección

El panel de control incluye HTML y JavaScript para recuperar los datos de notificación y los datos de video adicionales de la 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.renditons + '</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 for Brightcove RESTful APIs
    * gets an access token, makes the request, and returns the response
    * Accessing:
    *     URL: https://solutions.brightcove.com/bcls/bcls-proxy/bcsl-proxy.php
    *         (note you should *always* access the proxy via HTTPS)
    *     Method: POST
    *
    * @post {string} url - the URL for the API request
    * @post {string} [requestType=GET] - HTTP method for the request
    * @post {string} [requestBody=null] - JSON data to be sent with write requests
    *
    * @returns {string} $response - JSON response received from the API
    */
    // CORS enablement
    header("Access-Control-Allow-Origin: *");
    // set up request for access token
    $data = array();
    //
    // change the values below to use this proxy with a different account
    //
    $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);
    // Check for errors
    if ($response === FALSE) {
    die(curl_error($ch));
    }
    // Decode the response
    $responseData = json_decode($response, TRUE);
    $access_token = $responseData["access_token"];
    // set up the API call
    // get data
    if ($_POST["requestBody"]) {
    $data = json_decode($_POST["requestBody"]);
    } else {
    $data = array();
    }
    // get request type or default to GET
    if ($_POST["requestType"]) {
    $method = $_POST["requestType"];
    } else {
    $method = "GET";
    }
    // get the URL and authorization info from the form data
    $request = $_POST["url"];
    //send the http request
    $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',
    "Authorization: Bearer {$access_token}",
    ),
    CURLOPT_POSTFIELDS => json_encode($data)
    ));
    $response = curl_exec($ch);
    curl_close($ch);
    // Check for errors
    if ($response === FALSE) {
    echo "Error: "+$response;
    die(curl_error($ch));
    }
    // Decode the response
    // $responseData = json_decode($response, TRUE);
    // return the response to the AJAX caller
    echo $response;
    ?>
    
    

Borrar el registro

Esta simple aplicación de 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 'Log file cleared - <a href="di-log.html">go back to the dashboard</a>';
    ?>
    
    

Página actualizada por última vez el 12 jun 2020