簡介
本文檔介紹瞭如何通過向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 在這些事件發生時通知您,而不是必須不斷詢問狀態,直到您得到所需的答案。現在,我們將研究您如何圍繞處理通知構建一個應用程序。
當視頻準備就緒時,動態攝取通知會為您提供所有您需要知道的信息-您只需要知道要查找的內容...並定義“就緒”對系統意味著什麼。此圖總結了工作流程:
動態內嵌通知
動態攝取通知服務向您發送有關多種事件的通知。對於確定視頻“就緒”的時間,最有用的兩個是指示已創建特定移演的音頻,而指示所有處理已完成的音頻。以下是每個示例:
動態再現創建的通知
在此示例中註意:
- 這
videoId
value 讓您知道演繹是針對哪個視頻的(如果您有多個正在運行的攝取作業) - 這
entity
值是創建的動態再現類型 - 如果
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>”;
?>