서문
이 문서에서는 작업 상태를 추적하는 방법에 대해 설명합니다. CMS API또는 사용하여Dynamic Ingest API알림 . 프로세스를 자동화하는 샘플 대시 보드 앱도 제공합니다.
수집 작업의 상태는 지난 7 일 동안 제출 된 작업에만 사용할 수 있습니다.
상태 요청
이러한 엔드포인트를 사용하여 동적 인제스트 작업 (수집, 교체 또는 재트랜스코드) 의 상태를 확인할 수 있습니다. 참고로이러한CMS API엔드포인트는Dynamic Delivery 작업에만 작동합니다 .
모든 작업에 대한 상태 가져 오기
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에서 알림을 받도록 하는 것이 좋습니다. 이제 알림 처리를 중심으로 앱을 빌드하는 방법을 살펴 보겠습니다.
Dynamic Ingest 알림은 비디오가 준비되었을 때 알아야하는 모든 정보를 제공합니다. 무엇을 찾아야하는지 알고 시스템에 "준비"가 무엇을 의미하는지 정의하기 만하면됩니다. 이 다이어그램은 워크 플로우를 요약합니다.
동적 인제스트 알림
Dynamic Ingest 알림 서비스는 여러 종류의 이벤트에 대한 알림을 보냅니다. 비디오가 "준비된"시기를 파악하는 데 가장 유용한 두 가지는 특정 변환이 생성되었음을 나타내는 것과 모든 처리가 완료되었음을 나타내는 것입니다. 다음은 각각의 예입니다.
동적 변환 생성 알림
이 예제의 참고 사항은 다음과 같습니다.
videoId
값을 통해 어떤 비디오에 대한 렌디션인지 알 수 있습니다 (여러 인제스트 작업이 실행 중인 경우).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 파일의 각 비디오에 대한 개체 배열에 비디오 알림을 추가합니다. 대시 보드 자체는 알림 데이터를 가져 오기 위해 JSON 파일을 가져 오는 HTML 페이지입니다. ID를 사용하여 CMS API에 비디오 메타데이터를가져오도록 요청합니다.
다음은 앱의 상위 수준 아키텍처입니다.
앱 부분
알림 핸들러는 PHP로 빌드됩니다. 완전한 알림 처리를 찾고 별도의 자바 스크립트 파일의 배열에 동영상 ID를 추가합니다.
< ?PHP
//JSON 데이터에 대해 POST가 작동하지 않음
$ 문제 = “오류 없음”;
{
$json = 파일_겟_콘텐츠 ('php: //입력');
$ 디코딩 = json_decode ($ JSON, 사실);
} catch (Exception $e) {
$ 문제 = $ - > getMessage ();
echo $ problem;
}
// 전체 알림
$ 알림 = json_인코딩 ($ 디코딩, JSON_PRETTY_인쇄);
// 알림의 유용한 부분을 추출하여 시작합니다.
// Dynamic Delivery의 경우 'videoId'를 찾습니다.
if (잘못 설정됨 ($디코딩됨 ["비디오 ID"]) {
$비디오ID = $디코딩됨 ["비디오ID"];
} elseif (isset ($디코딩된 ["엔티티"])) {
$비디오ID = $디코딩됨 ["엔티티"];
} else {
$ videoId = null;
}
if (설정됨 ($ 디코딩됨 ["엔티티 유형"]) {
$ 엔티티 유형 = $ 디코딩된 ["엔티티 유형"];
} else {
$ entityType = null;
}
if (잘못 설정됨 ($ 디코딩됨 ["상태"]) {
$status = $디코딩됨 ["상태"];
} else {
$ status = null;
}
if (잘못 설정됨 ($디코딩된 ["액션"]) {
$액션 = $디코딩됨 ["액션"];
} else {
$ action = null;
}
// 완성 된 제목에 대한 알림 인 경우 조치
if (($ entityType == 'TITLE') && ($ action == 'CREATE')) {
if (($ status == 'SUCCESS') || ($ status == 'FAILED')) {
$ newLine = "\\ nvideoIdArray.unshift (". $ videoId. ");";
//PHP가 로그 파일을 찾을 수 있는 위치를 알려주고 PHP에 열도록 지시합니다.
//이전에 만든 문자열을 추가합니다.
$ logFileLocation = "video-ids.js";
$ 파일 핸들 = fopen ($ 로그 파일 위치, 'a') 또는 다이 (“-1");
chmod ($로그 파일위치, 0777);
fwrite ($ fileHandle, $ newLine);
fclose ($ fileHandle);
}
}
// 감사 추적에 대한 전체 알림 저장
$ logEntry = $ notification. ", \\ n";
$ logFileLocation = "full-log.txt";
$ 파일 핸들 = fopen ($ 로그 파일 위치, 'a') 또는 다이 (“-1");
chmod ($로그 파일위치, 0777);
fwrite ($ fileHandle, $ logEntry);
fclose ($ fileHandle);
echo "Dynamic Ingest 콜백 앱이 실행 중입니다.";
?>
JSON 파일:
JSON 파일은 처음에 빈 배열 ( []
) 이며 알림 핸들러에 의해 데이터가 추가됩니다.
대시보드
대시보드에는 에서 알림 데이터 및 추가 비디오 데이터를 가져와서 결과를 테이블에 기록할 수 있는 HTML CMS API및 JavaScript가 포함되어 있습니다.
<!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 - 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
* (HTTPS를 통해 *항상* 프록시에 액세스해야 한다는 점을 참고)
* 방법: 우편
*
* @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 강화
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 = “귀하_클라이언트_ID_HERE”;
$client_secret = "YOUR_CLIENT_SECRET_HERE";
$auth_string = “{$client_id}: {$클라이언트_비밀}”;
$request = "https://oauth.brightcove.com/v4/access_token?grant_type=client_credentials “;
$ch = 컬링 초기화 ($리퀘스트);
curl_setopt_array($ch, array(
컬롭트_포스트 => 참,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_USERPWD => $auth_string,
컬롭트_HTTP헤더 => 배열 (
'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_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE,
컬롭트_HTTP헤더 => 배열 (
'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 '로그 파일 삭제됨 - < href=” di-log.html “>대시보드로 돌아가기 < /a >';
?>