取得動態內嵌要求的狀態

當您使用Dynamic Ingest API要將視頻添加到您的視頻雲帳戶,您最想知道的是視頻的處理時間以及演繹版是否已成功創建。

簡介

本文檔介紹瞭如何通過向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或者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 value 讓您知道演繹是針對哪個視頻的(如果您有多個正在運行的攝取作業)
  • 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": ["https://host1/path1”, “https://host2/path2”]
    }
    
    

樣板儀表板

本部分說明如何將通知放在一起以為Dynamic Ingest API構建簡單的儀表板。通知處理程序解析來自Dynamic Ingest API識別處理完成通知。然後,它將視頻通知添加到JSON文件中每個視頻的對像數組中。儀表板本身是一個HTML頁面,該頁面導入JSON文件以獲取通知數據。它使用 ids 向內容管理系統接口獲取視頻元數據。

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

提取儀表板架構
提取儀表板架構

應用程式零件

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

    <?php
      //POST 不適用於 JSON 數據
      $ 問題 =「無錯誤」;
      嘗試{
        $ JSON = 文件的內容('PHP://輸入');
        $ 解碼 = 解碼($ JSON,真);
      } catch(Exception $ e){
        $ 問題 = $ 電子-> 獲取消息();
        迴聲$問題;
      }

      //完整通知
      $ 通知 = JSON_ 編碼($ 解碼,JSON_PRETY);

      //首先提取通知的有用部分
      //對於動態投放,請查找“ videoId”

      如果(isset($ decoded [“ videoId”])){
        $ videoId = $ decoded [“ videoId”];
      } elseif(isset($ decoded [“ entity”])){
        $ videoId = $ decoded [“ entity”];
      }其他{
        $ videoId = null;
      }

      如果(isset($ decoded [“ entityType”])){
        $ 實體類型 = $ 解碼 ["實體類型"];
      }其他{
        $ entityType = null;
      }

      如果(isset($ decoded [“ status”])){
        $ status = $ decoded [“ status”];
      }其他{
        $ status = null;
      }

      if(isset($ decoded [“ action”])){
        $ action = $ decoded [“ action”];
      }其他{
        $ action = null;
      }

      //如果通知是關於完整標題的,請採取行動

      if((($ entityType =='TITLE')&&($ action =='CREATE')){
        如果(($ status =='成功')||($ status =='失敗')){
          $ newLine =“ \\ nvideoIdArray.unshift(”。$ videoId。“);”;
          //告訴PHP 在哪裡可以找到日誌文件並告訴 PHP 打開它
          //並添加我們之前創建的字符串。
          $ logFileLocation =“ video-ids.js”;
          $ 文件句柄 = fopen($ 日誌文件位置,'A')或死亡(「-1」);
          ($ 日誌文件位置,0777);
          fwrite($ fileHandle,$ newLine);
          fclose($ fileHandle);
        }
      }

      //保存完整的通知以供審核跟踪
      $ logEntry = $ notification。“,\\ n”;

      $ logFileLocation =“ full-log.txt”;
      $ 文件句柄 = fopen($ 日誌文件位置,'A')或死亡(「-1」);
      ($ 日誌文件位置,0777);
      fwrite($ fileHandle,$ logEntry);
      fclose($ fileHandle);


      回顯“ Dynamic Ingest回調應用程序正在運行”;
      ?>
      
      

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

代理

    <?php
    /**
    * brightcove-learning-proxy.php-布萊特灣休息風格 API 的代理
    * 獲取訪問令牌,發出請求,並返回響應
    * 存取:
    * 網址:https://solutions.brightcove.com/bcls/bcls-proxy/bcsl-proxy.php
    *(請注意,您應該 * 總是 * 通過 HTTPS 訪問代理)
    * 方法:開機自檢
    *
    * @post {字符串} 網址-API 請求的 URL
    * @post {字符串} [請求類型 = 獲取]-請求的 HTTP 方法
    * @post {字符串} [請求體 = 空]-JSON 數據與寫入請求一起發送
    *
    * @returns {字符串} $ 響應-從 API 接收到的 JSON 響應
    */
    // CORS誘人
    header(“ Access-Control-Allow-Origin:*”);
    //設置訪問令牌的請求
    $ 數據 = 數組();
    //
    //變更下列值,將此代理伺服器用於不同的帳戶
    //
    $ 客戶端代碼 =「您的客戶端 _ ID」;
    $ 客戶機密 =「你的客戶 _ 秘密 _ 這裡」;
    $ 驗證字符串 =「{$ 客戶端}:{$ 客戶端 _ 秘密}」;
    請求 = "https://oauth.brightcove.com/v4/access_token?grant_type=client_credentials「;
    $ 查詢 = 初始化($ 請求);
    curl_setopt_array($ ch,array(
    CURLOPT_POST => TRUE,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_SSL_VERIFYPEER =>否,
    CURLOPT_USERPWD => $ auth_string,
    CURLOPT_HTTPHEADER =>數組(
    '內容類型:應用程序 /x-www 形式的 URL 代碼',
    )、
    CURLOPT_POSTFIELDS => $ data
    );
    $ 響應 = 捲曲($ 查詢);
    curl_close($ ch);
    //檢查錯誤
    if($ response === FALSE){
    die(curl_error($ ch));
    }
    //解碼響應
    $ 響應數據 = json_解碼($ 響應,真);
    $ 訪問令牌 = $ 響應數據 ["訪問令牌"];
    //設定 API 呼叫
    //取得資料
    如果($ _POST [“ requestBody”]){
    $ 數據 = 解碼($ _POST ["請求主體"]);
    }其他{
    $ 數據 = 數組();
    }
    //獲取請求類型或默認為 GET
    如果($ _POST [“ requestType”]){
    $ 方法 = $_POST ["請求類型"];
    }其他{
    $ 方法 =「獲取」;
    }
    //從表單數據獲取 URL 和授權信息
    $ 請求 = $ _ 郵政 ["網址"];
    //傳送http 請求
    $ 查詢 = 初始化($ 請求);
    curl_setopt_array($ ch,array(
    CURLOPT_CUSTOMREQUEST => $方法,
    CURLOPT_RETURNTRANSFER => TRUE,
    CURLOPT_SSL_VERIFYPEER =>否,
    CURLOPT_HTTPHEADER =>數組(
    '內容類型:應用程序/json',
    「授權:承載人 {$ 訪問令牌}」,
    )、
    CURLOPT_POSTFIELDS => json_encode($ data)
    );
    $ 響應 = 捲曲($ 查詢);
    curl_close($ ch);
    //檢查錯誤
    if($ response === FALSE){
    迴聲“錯誤:” + $響應;
    die(curl_error($ ch));
    }
    //解碼響應
    //$ 響應數據 = json_解碼($ 響應,真);
    //將響應返回給 AJAX 調用者
    echo $ response;
    ?>
    
    

清除記錄

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

    <?php
    $ 日誌文件位置 =「di.json」;
    $ 新鮮內容 = 數組();
    $ 編碼內容 = json_ 編碼($ 新鮮內容);
    file_put_contents($ logFileLocation,$ encodedContent);
    回顯“日誌文件已清除-<a href="di-log.html">返回儀表板</a>”;
    ?>