支持 聯繫支持 | 系統狀況 系統狀態

獲取動態提取請求的狀態

當您使用 Dynamic Ingest API 將視頻添加到您的 Video Cloud 帳戶,您最想知道的是何時處理了視頻以及是否成功創建了再現。

簡介

本文檔說明瞭如何通過向服務器發出狀態請求來跟踪作業的狀態 CMS API 或者通過使用 Dynamic Ingest API 通知。 我們還提供了一個示例儀表板應用程序,可自動執行該過程

請注意,提取作業的狀態僅適用於過去7天提交的作業。

要求狀態

您使用以下命令獲取動態攝取作業(攝取,替換或重新轉碼)的狀態 這些 CMS API 端點 -請注意,這些端點適用於 僅動態投放作業:

獲取所有職位的狀態

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

響應如下所示:

     [
      {
        "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"
      }
    ]

獲取特定職位的狀態

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

響應如下所示:

    {
        "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"
      }

的可能值 state 分別是:

  • processing:正在處理,視頻尚無法播放
  • publishing:已創建至少一個可播放的演繹文件,並且正在準備播放視頻
  • published:至少有一個演繹版本可供播放
  • finished:所有處理完成
  • failed:處理失敗; 如果您無法找出問題所在,請與支持小組聯繫

獲取通知

雖然上面討論的請求狀態方法有效,但是如果您正在等待特定狀態(published or finished),最好是在這些事件發生時讓Brightcove通知您,而不是一直詢問狀態,直到獲得所需的答案為止。 現在,我們將研究您如何圍繞處理通知來構建應用程序。

動態攝取通知為您提供了視頻準備就緒時所需的所有信息-您只需要知道要查找的內容...並定義“就緒”對系統的意義。 此圖總結了工作流程:

攝取狀態工作流程
攝取狀態工作流程

動態提取通知

動態攝取通知服務向您發送有關多種事件的通知。 對於確定視頻“就緒”的時間,最有用的兩個是表示已創建特定演繹形式的兩個,另一個是指示所有處理已完成的一個。 以下是每個示例:

動態再現創建的通知

    {
      "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"
    }
    
    
移交通知已創建

在此示例中註意:

  • videoId 值可讓您知道演繹對像是哪個視頻(如果您正在運行多個攝取作業)
  • entity 值是創建的動態演繹類型
  • 如果 status 值為“ SUCCESS”,表示創建成功

處理完成通知

    {
      "entity": "5660367449001",
      "entityType": "TITLE",
      "version": "1",
      "action": "CREATE",
      "jobId": "d3ef8751-2b88-4141-95d5-83f0393aca07",
      "videoId": "5660367449001",
      "accountId": "57838016001",
      "status": "SUCCESS"
    }
    
    
處理完成通知

在此示例中註意:

  • videoId jobId 值可讓您知道這是哪個視頻(如果您正在運行多個攝取作業)
  • 如果 status 值為“ SUCCESS”,視頻已成功處理

要接收通知,您需要在其中包含“回調”字段 Dynamic Ingest API 請求,指向一個或多個回調地址:

    {
      "master": {
      "url": "https://s3.amazonaws.com/bucket/mysourcevideo.mp4"
      }, "profile": "multi-platform-extended-static",
      "callbacks": ["http://host1/path1”, “http://host2/path2”]
    }
    
    

樣板儀表板

本節說明如何將通知放在一起以為 Dynamic Ingest API。 通知處理程序解析來自 Dynamic Ingest API 識別處理中的完整通知。 然後,它將視頻通知添加到JSON文件中每個視頻的對像數組中。 儀表板本身是一個HTML頁面,該頁面導入JSON文件以獲取通知數據。 它使用ID向 CMS API 獲取視頻元數據。

這是應用程序的高級架構:

攝取Dashboad架構
攝取Dashboad架構

應用程式零件

通知處理程序是用PHP內置的-它查找處理完整的通知,並將視頻ID添加到單獨JavaScript文件中的數組中:

    <?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";
      ?>
      
      

JSON文件:

JSON文件最初是一個空數組( [] )-數據由通知處理程序添加。

我的帳戶

資訊主頁包含HTML和JavaScript,可從中獲取通知數據和其他視頻數據 CMS API 並將結果寫到表中:

      <!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>
    
    

代理

    <?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;
    ?>
    
    

清除日誌

這個簡單的PHP應用程序只是將JavaScript文件恢復為原始狀態,清除了舊的視頻ID:

    <?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>';
    ?>
    
    

頁面最後更新於12年2020月XNUMX日