This is article 11 of the YouTube API With PHP series.
The insert API call is used to upload a Caption track. A maximum filesize of 100 Mb is allowed .
The Request URL is
POST https://www.googleapis.com/upload/youtube/v3/captions
Parameters:
- key (string) required. Your API key
- part (string) required. Comma separated. One of or all of the below:
- “id”
- “snippet”
- onBehalfOfContentOwner (string) optional. This is relevant only for YouTube Channel Partners. For this parameter, the API request URL should have user authentication.We will not be exploring this option.
- sync (boolean) if set to true then , YouTube will generate new timecodes for syncing the Caption with the Video. It will ignore any existing timecodes present in the Caption file. If set to false, YouTube will follow the timecodes present in the Caption file. When in doubt, always set this to true
Uploading a Caption track firstly requires the existence of a valid caption file. Mostly caption files are pure text files in either of the following formats: sbv, scc, srt, ttml, vtt.
The method to upload a Caption track requires two calls to the API url:
The first POST call sends the Caption resource as json-encoded data. We pass the querystring uploadType=resumable to tell YouTube that we will be uploading the actual file in the next call. If the first call works, then in the response header, we receive an upload Id.
We make the second POST call with the upload Id as part of the querystring. On successful execution the response will contain a json-encoded Caption resource.
This API call requires user-authentication, so it means that you can only upload captions for your videos.
Here is sample code which uploads a sample caption file called testcaption.en.vtt for a video.
<?php error_reporting(E_ALL ^ E_NOTICE ^ E_WARNING ^ E_DEPRECATED); set_time_limit(60 * 3); session_start(); $clientId = "6**"; $clientSecret = "**"; $g_youtubeDataAPIKey = "**"; $videoId = "tJEVea-1jWo"; $_SESSION["code_id"] = $_SERVER["PHP_SELF"]; if ($_SESSION["access_token"] == null || $_SESSION["access_token"] == "") { // check for oauth response header("Location: ../../init-login.php"); exit; } $accessToken = $_SESSION["access_token"]; $snippet = new Snippet(); $snippet->videoId = $videoId; $snippet->language = "en"; $snippet->name = "English Captions 3"; $snippet->isDraft = false; $resource = new CaptionResource(); $resource->snippet = $snippet; $resource->kind = "youtube#caption"; $data = $resource; $data = json_encode($data); // load caption file contents $fh = fopen("testcaption.en.vtt", "r"); if (!$fh) exit("Could not open caption file"); fclose($fh); $fileData = file_get_contents("testcaption.en.vtt"); $postData = $data; var_dump($postData); echo("<br>"); // make api request $url = "https://www.googleapis.com/upload/youtube/v3/captions?uploadType=resumable&part=snippet&sync=true&key=" . $g_youtubeDataAPIKey; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_HTTPHEADER=>array('Content-Type: application/json; charset=UTF-8', 'Authorization: OAuth ' . $accessToken, 'Content-Length: ' . strlen($postData), 'X-Upload-Content-Length: ' . filesize("testcaption.en.vtt"), 'x-upload-content-type: application/octet-stream' ), CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_HEADER=>1, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "../../cert/cacert.pem", CURLOPT_CAPATH => "../../cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_CUSTOMREQUEST=>"POST", CURLOPT_POSTFIELDS=>$postData )); $resp = curl_exec($curl); curl_close($curl); echo("<hr>"); // get response headers to extract Upload id. $uploadId = null; $splitResponse = explode("\n",$resp); $headers = array(); $headers['status']=$splitResponse[0]; array_shift($splitResponse); foreach($splitResponse as $part){ $middle=explode(":",$part); $headers[trim($middle[0])] = trim($middle[1]); } foreach($headers as $key=>$value) { echo($key . " =". $value . "<br>"); if ($key == "X-GUploader-UploadID") $uploadId = $value; } if ($uploadId == null) { exit("Error. Could not obtain Upload id"); } echo("Upload id=" . $uploadId . "<br>"); // prepare curl for actual caption file upload $url = "https://www.googleapis.com/upload/youtube/v3/captions?uploadType=resumable&part=snippet&sync=true&key=" . $g_youtubeDataAPIKey . "&upload_id=" . $uploadId; $curl = curl_init(); curl_setopt_array($curl, array( CURLOPT_HTTPHEADER=>array('Content-Type: application/octet-stream', 'Authorization: OAuth ' . $accessToken, 'Content-Length: ' . filesize("testcaption.en.vtt") ), CURLOPT_RETURNTRANSFER => 1, CURLOPT_URL => $url, CURLOPT_USERAGENT => 'YouTube API Tester', CURLOPT_SSL_VERIFYPEER => 1, CURLOPT_SSL_VERIFYHOST=> 0, CURLOPT_CAINFO => "../../cert/cacert.pem", CURLOPT_CAPATH => "../../cert/cacert.pem", CURLOPT_FOLLOWLOCATION => TRUE, CURLOPT_CUSTOMREQUEST=>"POST", CURLOPT_POSTFIELDS=>$fileData )); $resp = curl_exec($curl); curl_close($curl); var_dump($resp); //////////////////// class CaptionResource { public $snippet; public $kind; } class Snippet { public $videoId; public $language; public $name; public $isDraft; } ?>
Here is the output:
string(122) "{"snippet":{"videoId":"tJEVea-1jWo","language":"en","name":"English Captions 4","isDraft":false},"kind":"youtube#caption"}" status =HTTP/1.1 200 OK X-GUploader-UploadID =AEnB2UpOdWghRcyfS5RDQQ8cZaRirhnuyEuGRw7Y7gyTBa6IvpvkUUOAunxPhZbgBMgpWpQK-Vm76i9mrVw-OKD516gtyR2XAg Location =https Vary =X-Origin Cache-Control =no-cache, no-store, max-age=0, must-revalidate Pragma =no-cache Expires =Mon, 01 Jan 1990 00 Date =Thu, 25 May 2017 07 Content-Length =0 Server =UploadServer Content-Type =text/html; charset=UTF-8 Alt-Svc =quic=" = Upload id=AEnB2UpOdWghRcyfS5RDQQ8cZaRirhnuyEuGRw7Y7gyTBa6IvpvkUUOAunxPhZbgBMgpWpQK-Vm76i9mrVw-OKD516gtyR2XAg string(507) "{ "kind": "youtube#caption", "etag": "\"m2yskBQFythfE4irbTIeOgYYfBU/9z6lnNGMJMUt4e3t2EwC6Bwx8Xg\"", "id": "_CWhX_n18fB88k6rhuYQISa4BUOa7B0XIFFkQA9k59hkurqwm3YKx-He6TGce-vO", "snippet": { "videoId": "tJEVea-1jWo", "lastUpdated": "2017-05-25T07:27:00.084Z", "trackKind": "standard", "language": "en", "name": "English Captions 4", "audioTrackType": "unknown", "isCC": false, "isLarge": false, "isEasyReader": false, "isDraft": false, "isAutoSynced": true, "status": "syncing" } }
We set up the class structures for a Caption resource and we fill in the required attributes and then json encode them.
The first cURL call needs the following things:
- You cannot pass the OAuth token within the URL . If the data to be transferred is a file, then you need to pass the OAuth token in the headers. So we have added ‘Authorization OAuth xxxxx’ in the headers.
- Pass content-length in the header. This is the content length of the Caption data being sent.
- Pass X-Upload-Content-Length in the header. This is the length of the file that will be sent in the next call.
- Pass x-upload-content-type as application/octet-stream. This defines the mime-type of the file that will be uploaded in the second call.
- Set CURLOPT_HEADER to 1 as the upload Id will be passed back in the response headers. If you dont set this, then no headers will be returned.
On running the first API call, we parse the value of “X-GUploader-UploadID” from the response header. This will only exist if the first call worked. At this stage, YouTube has received the Caption resource metadata and is waiting for the actual Caption contents.
The second cURL call needs the following things:
- Pass ‘Content-Type: application/octet-stream’ in the header.
- Pass ‘Authorization OAuth xxxxx’ in the header as in the first call.
- Set ‘Content-Length’ in the header to the length of the file being uploaded.
- Set POSTFIELDS to the contents read from the file
On successful execution, a Caption resource will be returned. You have to make sure that the name you give to the Caption file is unique. So if you already have a caption track called “My Captions” and you insert another one with the same name, the API call will fail with the message:
“A video can have multiple tracks for the same language, but each track must have a different name.”
Leave a Reply