Captions: download

Downloads a caption track. The caption track is returned in its original format unless the request specifies a value for the tfmt parameter and in its original language unless the request specifies a value for the tlang parameter.

Quota impact: A call to this method has a quota cost of approximately 200 units.

Request

HTTP request

GET https://www.googleapis.com/youtube/v3/captions/id

Authorization

This request requires authorization with at least one of the following scopes (read more about authentication and authorization).

Scope
https://www.googleapis.com/auth/youtube.force-ssl
https://www.googleapis.com/auth/youtubepartner

Parameters

The table below lists the parameters that this query supports. All of the parameters listed are query parameters.

Parameters
Required parameters
id string
The id parameter identifies the caption track that is being retrieved. The value is a caption track ID as identified by the id property in a caption resource.
Optional parameters
onBehalfOfContentOwner string
This parameter can only be used in a properly authorized request. Note: This parameter is intended exclusively for YouTube content partners.

The onBehalfOfContentOwner parameter indicates that the request's authorization credentials identify a YouTube CMS user who is acting on behalf of the content owner specified in the parameter value. This parameter is intended for YouTube content partners that own and manage many different YouTube channels. It allows content owners to authenticate once and get access to all their video and channel data, without having to provide authentication credentials for each individual channel. The actual CMS account that the user authenticates with must be linked to the specified YouTube content owner.
tfmt string
The tfmt parameter specifies that the caption track should be returned in a specific format. If the parameter is not included in the request, the track is returned in its original format.

Supported values are:
  • sbv – SubViewer subtitle
  • scc – Scenarist Closed Caption format
  • srt – SubRip subtitle
  • ttml – Timed Text Markup Language caption
  • vtt – Web Video Text Tracks caption
tlang string
The tlang parameter specifies that the API response should return a translation of the specified caption track. The parameter value is an ISO 639-1 two-letter language code that identifies the desired caption language. The translation is generated by using machine translation, such as Google Translate.

Request body

Do not provide a request body when calling this method.

Response

If successful, this method returns a binary file. The Content-Type header for the response is application/octet-stream.

Examples

Note: The following code samples may not represent all supported programming languages. See the client libraries documentation for a list of supported languages.

Java

This sample demonstrates how to use the following API methods to create and manage YouTube video caption tracks:
  • It calls the captions.insert method with the isDraft parameter set to true to upload a caption track in draft status.
  • It calls the captions.list method with the videoId parameter to retrieve video caption tracks.
  • It calls the captions.update method with the caption in the request body to update a caption track.
  • It calls the captions.download method to download the caption track.
  • It calls the captions.delete method to delete the caption track, using the id parameter to identify the caption track.

This example uses the Java client library.

/*
 * Copyright (c) 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.api.services.samples.youtube.cmdline.data;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.googleapis.media.MediaHttpDownloader;
import com.google.api.client.googleapis.media.MediaHttpDownloaderProgressListener;
import com.google.api.client.googleapis.media.MediaHttpUploader;
import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;
import com.google.api.client.http.InputStreamContent;
import com.google.api.services.samples.youtube.cmdline.Auth;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTube.Captions.Download;
import com.google.api.services.youtube.YouTube.Captions.Insert;
import com.google.api.services.youtube.YouTube.Captions.Update;
import com.google.api.services.youtube.model.Caption;
import com.google.api.services.youtube.model.CaptionListResponse;
import com.google.api.services.youtube.model.CaptionSnippet;
import com.google.common.collect.Lists;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.List;

/**
 * This sample creates and manages caption tracks by:
 *
 * 1. Uploading a caption track for a video via "captions.insert" method.
 * 2. Getting the caption tracks for a video via "captions.list" method.
 * 3. Updating an existing caption track via "captions.update" method.
 * 4. Download a caption track via "captions.download" method.
 * 5. Deleting an existing caption track via "captions.delete" method.
 *
 * @author Ibrahim Ulukaya
 */
public class Captions {

    /**
     * Define a global instance of a YouTube object, which will be used to make
     * YouTube Data API requests.
     */
    private static YouTube youtube;

    /**
     * Define a global variable that specifies the MIME type of the caption
     * being uploaded.
     */
    private static final String CAPTION_FILE_FORMAT = "*/*";

    /**
     * Define a global variable that specifies the caption download format.
     */
    private static final String SRT = "srt";


    /**
     * Upload, list, update, download, and delete caption tracks.
     *
     * @param args command line args (not used).
     */
    public static void main(String[] args) {

        // This OAuth 2.0 access scope allows for full read/write access to the
        // authenticated user's account and requires requests to use an SSL connection.
        List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/youtube.force-ssl");

        try {
            // Authorize the request.
            Credential credential = Auth.authorize(scopes, "captions");

            // This object is used to make YouTube Data API requests.
            youtube = new YouTube.Builder(Auth.HTTP_TRANSPORT, Auth.JSON_FACTORY, credential)
                    .setApplicationName("youtube-cmdline-captions-sample").build();

            // Prompt the user to specify the action of the be achieved.
            String actionString = getActionFromUser();
            System.out.println("You chose " + actionString + ".");

            Action action = Action.valueOf(actionString.toUpperCase());
            switch (action) {
              case UPLOAD:
                uploadCaption(getVideoId(), getLanguage(), getName(), getCaptionFromUser());
                break;
              case LIST:
                listCaptions(getVideoId());
                break;
              case UPDATE:
                updateCaption(getCaptionIDFromUser(), getUpdateCaptionFromUser());
                break;
              case DOWNLOAD:
                downloadCaption(getCaptionIDFromUser());
                break;
              case DELETE:
                deleteCaption(getCaptionIDFromUser());
                break;
              default:
                // All the available methods are used in sequence just for the sake
                // of an example.

                //Prompt the user to specify a video to upload the caption track for and
                // a language, a name, a binary file for the caption track. Then upload the
                // caption track with the values that are selected by the user.
                String videoId = getVideoId();
                uploadCaption(videoId, getLanguage(), getName(), getCaptionFromUser());
                List<Caption> captions = listCaptions(videoId);
                if (captions.isEmpty()) {
                    System.out.println("Can't get video caption tracks.");
                } else {
                    // Retrieve the first uploaded caption track.
                    String firstCaptionId = captions.get(0).getId();

                    updateCaption(firstCaptionId, null);
                    downloadCaption(firstCaptionId);
                    deleteCaption(firstCaptionId);
                }
            }
        } catch (GoogleJsonResponseException e) {
            System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode()
                    + " : " + e.getDetails().getMessage());
            e.printStackTrace();

        } catch (IOException e) {
            System.err.println("IOException: " + e.getMessage());
            e.printStackTrace();
        } catch (Throwable t) {
            System.err.println("Throwable: " + t.getMessage());
            t.printStackTrace();
        }
    }

    /**
     * Deletes a caption track for a YouTube video. (captions.delete)
     *
     * @param captionId The id parameter specifies the caption ID for the resource
     * that is being deleted. In a caption resource, the id property specifies the
     * caption track's ID.
     * @throws IOException
     */
    private static void deleteCaption(String captionId) throws IOException {
      // Call the YouTube Data API's captions.delete method to
      // delete an existing caption track.
      youtube.captions().delete(captionId);
      System.out.println("  -  Deleted caption: " + captionId);
    }

    /**
     * Downloads a caption track for a YouTube video. (captions.download)
     *
     * @param captionId The id parameter specifies the caption ID for the resource
     * that is being downloaded. In a caption resource, the id property specifies the
     * caption track's ID.
     * @throws IOException
     */
    private static void downloadCaption(String captionId) throws IOException {
      // Create an API request to the YouTube Data API's captions.download
      // method to download an existing caption track.
      Download captionDownload = youtube.captions().download(captionId).setTfmt(SRT);

      // Set the download type and add an event listener.
      MediaHttpDownloader downloader = captionDownload.getMediaHttpDownloader();

      // Indicate whether direct media download is enabled. A value of
      // "True" indicates that direct media download is enabled and that
      // the entire media content will be downloaded in a single request.
      // A value of "False," which is the default, indicates that the
      // request will use the resumable media download protocol, which
      // supports the ability to resume a download operation after a
      // network interruption or other transmission failure, saving
      // time and bandwidth in the event of network failures.
      downloader.setDirectDownloadEnabled(false);

      // Set the download state for the caption track file.
      MediaHttpDownloaderProgressListener downloadProgressListener = new MediaHttpDownloaderProgressListener() {
          @Override
          public void progressChanged(MediaHttpDownloader downloader) throws IOException {
              switch (downloader.getDownloadState()) {
                  case MEDIA_IN_PROGRESS:
                      System.out.println("Download in progress");
                      System.out.println("Download percentage: " + downloader.getProgress());
                      break;
                  // This value is set after the entire media file has
                  //  been successfully downloaded.
                  case MEDIA_COMPLETE:
                      System.out.println("Download Completed!");
                      break;
                  // This value indicates that the download process has
                  //  not started yet.
                  case NOT_STARTED:
                      System.out.println("Download Not Started!");
                      break;
              }
          }
      };
      downloader.setProgressListener(downloadProgressListener);

      OutputStream outputFile = new FileOutputStream("captionFile.srt");
      // Download the caption track.
      captionDownload.executeAndDownloadTo(outputFile);
    }

    /**
     * Updates a caption track's draft status to publish it.
     * Updates the track with a new binary file as well if it is present.  (captions.update)
     *
     * @param captionId The id parameter specifies the caption ID for the resource
     * that is being updated. In a caption resource, the id property specifies the
     * caption track's ID.
     * @param captionFile caption track binary file.
     * @throws IOException
     */
    private static void updateCaption(String captionId, File captionFile) throws IOException {
      // Modify caption's isDraft property to unpublish a caption track.
      CaptionSnippet updateCaptionSnippet = new CaptionSnippet();
      updateCaptionSnippet.setIsDraft(false);
      Caption updateCaption = new Caption();
      updateCaption.setId(captionId);
      updateCaption.setSnippet(updateCaptionSnippet);
      
      Caption captionUpdateResponse;

      if (captionFile == null) {
        // Call the YouTube Data API's captions.update method to update an existing caption track.
        captionUpdateResponse = youtube.captions().update("snippet", updateCaption).execute();

      } else {
        // Create an object that contains the caption file's contents.
        InputStreamContent mediaContent = new InputStreamContent(
                CAPTION_FILE_FORMAT, new BufferedInputStream(new FileInputStream(captionFile)));
        mediaContent.setLength(captionFile.length());

        // Create an API request that specifies that the mediaContent
        // object is the caption of the specified video.
        Update captionUpdate = youtube.captions().update("snippet", updateCaption, mediaContent);

        // Set the upload type and add an event listener.
        MediaHttpUploader uploader = captionUpdate.getMediaHttpUploader();

        // Indicate whether direct media upload is enabled. A value of
        // "True" indicates that direct media upload is enabled and that
        // the entire media content will be uploaded in a single request.
        // A value of "False," which is the default, indicates that the
        // request will use the resumable media upload protocol, which
        // supports the ability to resume an upload operation after a
        // network interruption or other transmission failure, saving
        // time and bandwidth in the event of network failures.
        uploader.setDirectUploadEnabled(false);

        // Set the upload state for the caption track file.
        MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
            @Override
            public void progressChanged(MediaHttpUploader uploader) throws IOException {
                switch (uploader.getUploadState()) {
                    // This value is set before the initiation request is
                    // sent.
                    case INITIATION_STARTED:
                        System.out.println("Initiation Started");
                        break;
                    // This value is set after the initiation request
                    //  completes.
                    case INITIATION_COMPLETE:
                        System.out.println("Initiation Completed");
                        break;
                    // This value is set after a media file chunk is
                    // uploaded.
                    case MEDIA_IN_PROGRESS:
                        System.out.println("Upload in progress");
                        System.out.println("Upload percentage: " + uploader.getProgress());
                        break;
                    // This value is set after the entire media file has
                    //  been successfully uploaded.
                    case MEDIA_COMPLETE:
                        System.out.println("Upload Completed!");
                        break;
                    // This value indicates that the upload process has
                    //  not started yet.
                    case NOT_STARTED:
                        System.out.println("Upload Not Started!");
                        break;
                }
            }
        };
        uploader.setProgressListener(progressListener);

        // Upload the caption track.
        captionUpdateResponse = captionUpdate.execute();
        System.out.println("\n================== Uploaded New Caption Track ==================\n");
      }
      
      // Print information from the API response.
      System.out.println("\n================== Updated Caption Track ==================\n");
      CaptionSnippet snippet = captionUpdateResponse.getSnippet();
      System.out.println("  - ID: " + captionUpdateResponse.getId());
      System.out.println("  - Name: " + snippet.getName());
      System.out.println("  - Language: " + snippet.getLanguage());
      System.out.println("  - Draft Status: " + snippet.getIsDraft());
      System.out.println("\n-------------------------------------------------------------\n");
    }

    /**
     * Returns a list of caption tracks. (captions.listCaptions)
     *
     * @param videoId The videoId parameter instructs the API to return the
     * caption tracks for the video specified by the video id.
     * @throws IOException
     */
    private static List<Caption> listCaptions(String videoId) throws IOException {
      // Call the YouTube Data API's captions.list method to
      // retrieve video caption tracks.
      CaptionListResponse captionListResponse = youtube.captions().
          list("snippet", videoId).execute();

      List<Caption> captions = captionListResponse.getItems();
      // Print information from the API response.
      System.out.println("\n================== Returned Caption Tracks ==================\n");
      CaptionSnippet snippet;
      for (Caption caption : captions) {
          snippet = caption.getSnippet();
          System.out.println("  - ID: " + caption.getId());
          System.out.println("  - Name: " + snippet.getName());
          System.out.println("  - Language: " + snippet.getLanguage());
          System.out.println("\n-------------------------------------------------------------\n");
      }

      return captions;
    }

    /**
     * Uploads a caption track in draft status that matches the API request parameters.
     * (captions.insert)
     *
     * @param videoId the YouTube video ID of the video for which the API should
     *  return caption tracks.
     * @param captionLanguage language of the caption track.
     * @param captionName name of the caption track.
     * @param captionFile caption track binary file.
     * @throws IOException
     */
    private static void uploadCaption(String videoId, String captionLanguage,
        String captionName, File captionFile) throws IOException {
      // Add extra information to the caption before uploading.
      Caption captionObjectDefiningMetadata = new Caption();

      // Most of the caption's metadata is set on the CaptionSnippet object.
      CaptionSnippet snippet = new CaptionSnippet();

      // Set the video, language, name and draft status of the caption.
      snippet.setVideoId(videoId);
      snippet.setLanguage(captionLanguage);
      snippet.setName(captionName);
      snippet.setIsDraft(true);

      // Add the completed snippet object to the caption resource.
      captionObjectDefiningMetadata.setSnippet(snippet);

      // Create an object that contains the caption file's contents.
      InputStreamContent mediaContent = new InputStreamContent(
              CAPTION_FILE_FORMAT, new BufferedInputStream(new FileInputStream(captionFile)));
      mediaContent.setLength(captionFile.length());

      // Create an API request that specifies that the mediaContent
      // object is the caption of the specified video.
      Insert captionInsert = youtube.captions().insert("snippet", captionObjectDefiningMetadata, mediaContent);

      // Set the upload type and add an event listener.
      MediaHttpUploader uploader = captionInsert.getMediaHttpUploader();

      // Indicate whether direct media upload is enabled. A value of
      // "True" indicates that direct media upload is enabled and that
      // the entire media content will be uploaded in a single request.
      // A value of "False," which is the default, indicates that the
      // request will use the resumable media upload protocol, which
      // supports the ability to resume an upload operation after a
      // network interruption or other transmission failure, saving
      // time and bandwidth in the event of network failures.
      uploader.setDirectUploadEnabled(false);

      // Set the upload state for the caption track file.
      MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
          @Override
          public void progressChanged(MediaHttpUploader uploader) throws IOException {
              switch (uploader.getUploadState()) {
                  // This value is set before the initiation request is
                  // sent.
                  case INITIATION_STARTED:
                      System.out.println("Initiation Started");
                      break;
                  // This value is set after the initiation request
                  //  completes.
                  case INITIATION_COMPLETE:
                      System.out.println("Initiation Completed");
                      break;
                  // This value is set after a media file chunk is
                  // uploaded.
                  case MEDIA_IN_PROGRESS:
                      System.out.println("Upload in progress");
                      System.out.println("Upload percentage: " + uploader.getProgress());
                      break;
                  // This value is set after the entire media file has
                  //  been successfully uploaded.
                  case MEDIA_COMPLETE:
                      System.out.println("Upload Completed!");
                      break;
                  // This value indicates that the upload process has
                  //  not started yet.
                  case NOT_STARTED:
                      System.out.println("Upload Not Started!");
                      break;
              }
          }
      };
      uploader.setProgressListener(progressListener);

      // Upload the caption track.
      Caption uploadedCaption = captionInsert.execute();

      // Print the metadata of the uploaded caption track.
      System.out.println("\n================== Uploaded Caption Track ==================\n");
      snippet = uploadedCaption.getSnippet();
      System.out.println("  - ID: " + uploadedCaption.getId());
      System.out.println("  - Name: " + snippet.getName());
      System.out.println("  - Language: " + snippet.getLanguage());
      System.out.println("  - Status: " + snippet.getStatus());
      System.out
          .println("\n-------------------------------------------------------------\n");
    }

    /*
     * Prompt the user to enter a caption track ID. Then return the ID.
     */
    private static String getCaptionIDFromUser() throws IOException {

        String captionId = "";

        System.out.print("Please enter a caption track id: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        captionId = bReader.readLine();

        System.out.println("You chose " + captionId + ".");
        return captionId;
    }

    /*
     * Prompt the user to enter a video ID. Then return the ID.
     */
    private static String getVideoId() throws IOException {

        String videoId = "";

        System.out.print("Please enter a video id: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        videoId = bReader.readLine();

        System.out.println("You chose " + videoId + " for captions.");
        return videoId;
    }

    /*
     * Prompt the user to enter a name for the caption track. Then return the name.
     */
    private static String getName() throws IOException {

        String name = "";

        System.out.print("Please enter a caption track name: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        name = bReader.readLine();

        if (name.length() < 1) {
            // If nothing is entered, defaults to "YouTube For Developers".
            name = "YouTube for Developers";
        }

        System.out.println("You chose " + name + " as caption track name.");
        return name;
    }

    /*
     * Prompt the user to enter a language for the caption track. Then return the language.
     */
    private static String getLanguage() throws IOException {

        String language = "";

        System.out.print("Please enter the caption language: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        language = bReader.readLine();

        if (language.length() < 1) {
            // If nothing is entered, defaults to "en".
            language = "en";
        }

        System.out.println("You chose " + language + " as caption track language.");
        return language;
    }

    /*
     * Prompt the user to enter the path for the caption track file being uploaded.
     */
    private static File getCaptionFromUser() throws IOException {

        String path = "";

        System.out.print("Please enter the path of the caption track file to upload: ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        path = bReader.readLine();

        if (path.length() < 1) {
            // Exit if the user does not provide a path to the file.
            System.out.print("Path can not be empty!");
            System.exit(1);
        }

        File captionFile = new File(path);
        System.out.println("You chose " + captionFile + " to upload.");

        return captionFile;
    }

    /*
     * Prompt the user to enter the path for the caption track file being replaced.
     */
    private static File getUpdateCaptionFromUser() throws IOException {

        String path = "";

        System.out.print("Please enter the path of the new caption track file to upload"
            + " (Leave empty if you don't want to upload a new file.):");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        path = bReader.readLine();

        if (path.length() < 1) {
            return null;
        }

        File captionFile = new File(path);
        System.out.println("You chose " + captionFile + " to upload.");

        return captionFile;
    }

    /*
     * Prompt the user to enter an action. Then return the action.
     */
    private static String getActionFromUser() throws IOException {

        String action = "";

        System.out.print("Please choose action to be accomplished: ");
        System.out.print("Options are: 'upload', 'list', 'update', 'download', 'delete',"
            + " and 'all' ");
        BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
        action = bReader.readLine();

        return action;
    }

    public enum Action {
      UPLOAD,
      LIST,
      UPDATE,
      DOWNLOAD,
      DELETE,
      ALL
    }
}

PHP

This sample demonstrates how to use the following API methods to create and manage YouTube video caption tracks:
  • It calls the captions.insert method with the isDraft parameter set to true to upload a caption track in draft status.
  • It calls the captions.list method with the videoId parameter to retrieve video caption tracks.
  • It calls the captions.update method with the caption in the request body to update a caption track.
  • It calls the captions.download method to download the caption track.
  • It calls the captions.delete method to delete the caption track, using the id parameter to identify the caption track.

This example uses the PHP client library.

<?php

/**
 * This sample creates and manages caption tracks by:
 *
 * 1. Uploading a caption track for a video via "captions.insert" method.
 * 2. Getting the caption tracks for a video via "captions.list" method.
 * 3. Updating an existing caption track via "captions.update" method.
 * 4. Download a caption track via "captions.download" method.
 * 5. Deleting an existing caption track via "captions.delete" method.
 *
 * @author Ibrahim Ulukaya
 */

/**
 * Library Requirements
 *
 * 1. Install composer (https://getcomposer.org)
 * 2. On the command line, change to this directory (api-samples/php)
 * 3. Require the google/apiclient library
 *    $ composer require google/apiclient:~2.0
 */
if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
  throw new \Exception('please run "composer require google/apiclient:~2.0" in "' . __DIR__ .'"');
}

require_once __DIR__ . '/vendor/autoload.php';
session_start();

$htmlBody = <<<END
<form method="POST" enctype="multipart/form-data">
  <div>
    Action:
    <select id="action" name="action">
      <option value="upload">Upload - Fill in: video ID, caption track name, language and file</option>
      <option value="list">List - Fill in: video ID</option>
      <option value="update">Update - Fill in: caption track ID, (optional - caption track file)</option>
      <option value="download">Download - Fill in: caption track ID</option>
      <option value="delete">Delete - Fill in: caption track ID</option>
      <option value="all">All - Fill in: video ID, caption track name, language and file</option>
    </select>
  </div>
  <br>
  <div>
    Video ID: <input type="text" id="videoId" name="videoId" placeholder="Enter Video ID">
  </div>
  <br>
  <div>
    Caption Track Name: <input type="text" id="captionName" name="captionName" placeholder="Enter Caption Track Name">
  </div>
  <br>
  <div>
    Caption Track Language: <input type="text" id="captionLanguage" name="captionLanguage" placeholder="Enter Caption Track Language">
  </div>
  <br>
  <div>
    File: <input type="file" id ="captionFile" name="captionFile" accept="*/*">
  </div>
  <br>
  <div>
    Caption Track Id: <input type="text" id="captionId" name="captionId" placeholder="Enter Caption Track ID">
  </div>
  <br>
  <input type="submit" value="GO!">
</form>
END;

/*
 * You can acquire an OAuth 2.0 client ID and client secret from the
 * {{ Google Cloud Console }} <{{ https://cloud.google.com/console }}>
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 * <https://developers.google.com/youtube/v3/guides/authentication>
 * Please ensure that you have enabled the YouTube Data API for your project.
 */
$OAUTH2_CLIENT_ID = 'REPLACE_ME';
$OAUTH2_CLIENT_SECRET = 'REPLACE_ME';

$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);

/*
 * This OAuth 2.0 access scope allows for full read/write access to the
 * authenticated user's account and requires requests to use an SSL connection.
 */
$client->setScopes('https://www.googleapis.com/auth/youtube.force-ssl');
$redirect = filter_var('http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'],
    FILTER_SANITIZE_URL);
$client->setRedirectUri($redirect);

// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);

// Check if an auth token exists for the required scopes
$tokenSessionKey = 'token-' . $client->prepareScopes();
if (isset($_GET['code'])) {
  if (strval($_SESSION['state']) !== strval($_GET['state'])) {
    die('The session state did not match.');
  }

  $client->authenticate($_GET['code']);
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
  header('Location: ' . $redirect);
}

if (isset($_SESSION[$tokenSessionKey])) {
  $client->setAccessToken($_SESSION[$tokenSessionKey]);
}

// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
  // This code executes if the user enters an action in the form
  // and submits the form. Otherwise, the page displays the form above.
  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $videoId = isset($_POST['videoId']) ? $_POST['videoId'] : null;
    $captionFile = isset($_FILES['captionFile']) ? $_FILES['captionFile']['tmp_name'] : null;
    $captionName = isset($_POST['captionName']) ? $_POST['captionName'] : null;
    $captionLanguage = isset($_POST['captionLanguage']) ? $_POST['captionLanguage'] : null;
    $captionId = isset($_POST['captionId']) ? $_POST['captionId'] : null;
    try {
      switch ($_POST['action']) {
        case 'upload':
          uploadCaption($youtube, $client, $videoId, $captionFile,
              $captionName, $captionLanguage, $htmlBody);
          break;
        case 'list':
          $captions = listCaptions($youtube, $videoId, $htmlBody);
          break;
        case 'update':
          updateCaption($youtube, $client, $captionId, $htmlBody, $captionFile);
          break;
        case 'download':
          downloadCaption($youtube, $captionId, $htmlBody);
          break;
        case 'delete':
          deleteCaption($youtube, $captionId, $htmlBody);
          break;
        default:
          # All the available methods are used in sequence just for the sake of an example.
          uploadCaption($youtube, $client, $videoId, $captionFile,
              $captionName, $captionLanguage, $htmlBody);

          $captions = listCaptions($youtube, $videoId, $htmlBody);

          if (empty($captions)) {
            $htmlBody .= "<h3>Can't get video caption tracks.</h3>";
          } else {
            $firstCaptionId = $captions[0]['id'];
            updateCaption($youtube, $client, $firstCaptionId, $htmlBody, null);
            downloadCaption($youtube, $firstCaptionId, $htmlBody);
            deleteCaption($youtube, $firstCaptionId, $htmlBody);
        }
      }
    } catch (Google_Service_Exception $e) {
      $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
          htmlspecialchars($e->getMessage()));
    } catch (Google_Exception $e) {
      $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
          htmlspecialchars($e->getMessage()));
    }
  }
  $_SESSION[$tokenSessionKey] = $client->getAccessToken();
} elseif ($OAUTH2_CLIENT_ID == 'REPLACE_ME') {
  $htmlBody = <<<END
  <h3>Client Credentials Required</h3>
  <p>
    You need to set <code>\$OAUTH2_CLIENT_ID</code> and
    <code>\$OAUTH2_CLIENT_ID</code> before proceeding.
  <p>
END;
} else {
  // If the user hasn't authorized the app, initiate the OAuth flow
  $state = mt_rand();
  $client->setState($state);
  $_SESSION['state'] = $state;

  $authUrl = $client->createAuthUrl();
  $htmlBody = <<<END
  <h3>Authorization Required</h3>
  <p>You need to <a href="$authUrl">authorize access</a> before proceeding.<p>
END;
}

/**
 * Uploads a caption track in draft status that matches the API request parameters.
 * (captions.insert)
 *
 * @param Google_Service_YouTube $youtube YouTube service object.
 * @param Google_Client $client Google client.
 * @param $videoId the YouTube video ID of the video for which the API should
 *  return caption tracks.
 * @param $captionLanguage language of the caption track.
 * @param $captionName name of the caption track.
 * @param $captionFile caption track binary file.
 * @param $htmlBody html body.
 */
function uploadCaption(Google_Service_YouTube $youtube, Google_Client $client, $videoId,
    $captionFile, $captionName, $captionLanguage, &$htmlBody) {
    # Insert a video caption.
    # Create a caption snippet with video id, language, name and draft status.
    $captionSnippet = new Google_Service_YouTube_CaptionSnippet();
    $captionSnippet->setVideoId($videoId);
    $captionSnippet->setLanguage($captionLanguage);
    $captionSnippet->setName($captionName);

    # Create a caption with snippet.
    $caption = new Google_Service_YouTube_Caption();
    $caption->setSnippet($captionSnippet);

    // Specify the size of each chunk of data, in bytes. Set a higher value for
    // reliable connection as fewer chunks lead to faster uploads. Set a lower
    // value for better recovery on less reliable connections.
    $chunkSizeBytes = 1 * 1024 * 1024;

    // Setting the defer flag to true tells the client to return a request which can be called
    // with ->execute(); instead of making the API call immediately.
    $client->setDefer(true);

    // Create a request for the API's captions.insert method to create and upload a caption.
    $insertRequest = $youtube->captions->insert("snippet", $caption);

    // Create a MediaFileUpload object for resumable uploads.
    $media = new Google_Http_MediaFileUpload(
        $client,
        $insertRequest,
        '*/*',
        null,
        true,
        $chunkSizeBytes
    );
    $media->setFileSize(filesize($captionFile));


    // Read the caption file and upload it chunk by chunk.
    $status = false;
    $handle = fopen($captionFile, "rb");
    while (!$status && !feof($handle)) {
      $chunk = fread($handle, $chunkSizeBytes);
      $status = $media->nextChunk($chunk);
    }

    fclose($handle);

    // If you want to make other calls after the file upload, set setDefer back to false
    $client->setDefer(false);

    $htmlBody .= "<h2>Inserted video caption track for</h2><ul>";
    $captionSnippet = $status['snippet'];
    $htmlBody .= sprintf('<li>%s(%s) in %s language, %s status.</li>',
        $captionSnippet['name'], $status['id'], $captionSnippet['language'],
        $captionSnippet['status']);
    $htmlBody .= '</ul>';
}

/**
 * Returns a list of caption tracks. (captions.listCaptions)
 *
 * @param Google_Service_YouTube $youtube YouTube service object.
 * @param string $videoId The videoId parameter instructs the API to return the
 * caption tracks for the video specified by the video id.
 * @param $htmlBody - html body.
 */
function listCaptions(Google_Service_YouTube $youtube, $videoId, &$htmlBody) {
  // Call the YouTube Data API's captions.list method to retrieve video caption tracks.
  $captions = $youtube->captions->listCaptions("snippet", $videoId);

  $htmlBody .= "<h3>Video Caption Tracks</h3><ul>";
  foreach ($captions as $caption) {
    $htmlBody .= sprintf('<li>%s(%s) in %s language</li>', $caption['snippet']['name'],
        $caption['id'],  $caption['snippet']['language']);
  }
  $htmlBody .= '</ul>';

  return $captions;
}

/**
 * Updates a caption track's draft status to publish it.
 * Updates the track with a new binary file as well if it is present.  (captions.update)
 *
 * @param Google_Service_YouTube $youtube YouTube service object.
 * @param Google_Client $client Google client.
 * @param string $captionId The id parameter specifies the caption ID for the resource
 * that is being updated. In a caption resource, the id property specifies the
 * caption track's ID.
 * @param $htmlBody - html body.
 * @param $captionFile caption track binary file.
 */
function updateCaption(Google_Service_YouTube $youtube, Google_Client $client,
    $captionId, &$htmlBody, $captionFile) {
    // Modify caption's isDraft property to unpublish a caption track.
    $updateCaptionSnippet = new Google_Service_YouTube_CaptionSnippet();
    $updateCaptionSnippet->setIsDraft(true);

    # Create a caption with snippet.
    $updateCaption = new Google_Service_YouTube_Caption();
    $updateCaption->setSnippet($updateCaptionSnippet);
    $updateCaption->setId($captionId);

    if ($captionFile == '')
    {
      // Call the YouTube Data API's captions.update method to update an existing caption track.
      $captionUpdateResponse = $youtube->captions->update("snippet", $updateCaption);

      $htmlBody .= "<h2>Updated caption track</h2><ul>";
      $htmlBody .= sprintf('<li>%s(%s) draft status: %s</li>',
          $captionUpdateResponse['snippet']['name'],
      $captionUpdateResponse['id'],  $captionUpdateResponse['snippet']['isDraft']);
      $htmlBody .= '</ul>';
    } else {
      // Specify the size of each chunk of data, in bytes. Set a higher value for
      // reliable connection as fewer chunks lead to faster uploads. Set a lower
      // value for better recovery on less reliable connections.
      $chunkSizeBytes = 1 * 1024 * 1024;

      // Setting the defer flag to true tells the client to return a request which can be called
      // with ->execute(); instead of making the API call immediately.
      $client->setDefer(true);

      // Create a request for the YouTube Data API's captions.update method to update
      // an existing caption track.
      $captionUpdateRequest = $youtube->captions->update("snippet", $updateCaption);

      // Create a MediaFileUpload object for resumable uploads.
      $media = new Google_Http_MediaFileUpload(
          $client,
          $captionUpdateRequest,
          '*/*',
          null,
          true,
          $chunkSizeBytes
      );
      $media->setFileSize(filesize($captionFile));

      // Read the caption file and upload it chunk by chunk.
      $status = false;
      $handle = fopen($captionFile, "rb");
      while (!$status && !feof($handle)) {
        $chunk = fread($handle, $chunkSizeBytes);
        $status = $media->nextChunk($chunk);
      }

      fclose($handle);

      // If you want to make other calls after the file upload, set setDefer back to false
      $client->setDefer(false);

      $htmlBody .= "<h2>Updated caption track</h2><ul>";
      $htmlBody .= sprintf('<li>%s(%s) draft status: %s and updated the track with
          the new uploaded file.</li>',
          $status['snippet']['name'], $status['id'],  $status['snippet']['isDraft']);
      $htmlBody .= '</ul>';
    }
}

/**
 * Downloads a caption track for a YouTube video. (captions.download)
 *
 * @param Google_Service_YouTube $youtube YouTube service object.
 * @param string $captionId The id parameter specifies the caption ID for the resource
 * that is being downloaded. In a caption resource, the id property specifies the
 * caption track's ID.
 * @param $htmlBody - html body.
 */
function downloadCaption(Google_Service_YouTube $youtube, $captionId, &$htmlBody) {
    // Call the YouTube Data API's captions.download method to download an existing caption.
    $captionResouce = $youtube->captions->download($captionId, array(
        'tfmt' => "srt",
        'alt' => "media"
    ));

    $htmlBody .= "<h2>Downloaded caption track</h2><ul>";
    $htmlBody .= sprintf('<li>%s</li>',
      $captionResouce);
    $htmlBody .= '</ul>';
}

/**
 * Deletes a caption track for a YouTube video. (captions.delete)
 *
 * @param Google_Service_YouTube $youtube YouTube service object.
 * @param string $captionId The id parameter specifies the caption ID for the resource
 * that is being deleted. In a caption resource, the id property specifies the
 * caption track's ID.
 * @param $htmlBody - html body.
 */
function deleteCaption(Google_Service_YouTube $youtube, $captionId, &$htmlBody) {
    // Call the YouTube Data API's captions.delete method to delete a caption.
    $youtube->captions->delete($captionId);

    $htmlBody .= "<h2>Deleted caption track</h2><ul>";
    $htmlBody .= sprintf('<li>%s</li>',$captionId);
    $htmlBody .= '</ul>';
}
?>

<!doctype html>
<html>
<head>
<title>Create and manage video caption tracks</title>
</head>
<body>
  <?=$htmlBody?>
</body>
</html>

Python

This sample demonstrates how to use the following API methods to create and manage YouTube video caption tracks:
  • It calls the captions.insert method with the isDraft parameter set to true to upload a caption track in draft status.
  • It calls the captions.list method with the videoId parameter to retrieve video caption tracks.
  • It calls the captions.update method with the caption in the request body to update a caption track.
  • It calls the captions.download method to download the caption track.
  • It calls the captions.delete method to delete the caption track, using the id parameter to identify the caption track.

This example uses the Python client library.

#!/usr/bin/python

# Usage example:
# python captions.py --videoid='<video_id>' --name='<name>' --file='<file>' --language='<language>' --action='action'

import httplib2
import os
import sys

from apiclient.discovery import build_from_document
from apiclient.errors import HttpError
from oauth2client.client import flow_from_clientsecrets
from oauth2client.file import Storage
from oauth2client.tools import argparser, run_flow


# The CLIENT_SECRETS_FILE variable specifies the name of a file that contains

# the OAuth 2.0 information for this application, including its client_id and
# client_secret. You can acquire an OAuth 2.0 client ID and client secret from
# the {{ Google Cloud Console }} at
# {{ https://cloud.google.com/console }}.
# Please ensure that you have enabled the YouTube Data API for your project.
# For more information about using OAuth2 to access the YouTube Data API, see:
#   https://developers.google.com/youtube/v3/guides/authentication
# For more information about the client_secrets.json file format, see:
#   https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
CLIENT_SECRETS_FILE = "client_secrets.json"

# This OAuth 2.0 access scope allows for full read/write access to the
# authenticated user's account and requires requests to use an SSL connection.
YOUTUBE_READ_WRITE_SSL_SCOPE = "https://www.googleapis.com/auth/youtube.force-ssl"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"

# This variable defines a message to display if the CLIENT_SECRETS_FILE is
# missing.
MISSING_CLIENT_SECRETS_MESSAGE = """
WARNING: Please configure OAuth 2.0

To make this sample run you will need to populate the client_secrets.json file
found at:
   %s
with information from the APIs Console
https://console.developers.google.com

For more information about the client_secrets.json file format, please visit:
https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
""" % os.path.abspath(os.path.join(os.path.dirname(__file__),
                                   CLIENT_SECRETS_FILE))

# Authorize the request and store authorization credentials.
def get_authenticated_service(args):
  flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, scope=YOUTUBE_READ_WRITE_SSL_SCOPE,
    message=MISSING_CLIENT_SECRETS_MESSAGE)

  storage = Storage("%s-oauth2.json" % sys.argv[0])
  credentials = storage.get()

  if credentials is None or credentials.invalid:
    credentials = run_flow(flow, storage, args)

  # Trusted testers can download this discovery document from the developers page
  # and it should be in the same directory with the code.
  with open("youtube-v3-api-captions.json", "r") as f:
    doc = f.read()
    return build_from_document(doc, http=credentials.authorize(httplib2.Http()))


# Call the API's captions.list method to list the existing caption tracks.
def list_captions(youtube, video_id):
  results = youtube.captions().list(
    part="snippet",
    videoId=video_id
  ).execute()

  for item in results["items"]:
    id = item["id"]
    name = item["snippet"]["name"]
    language = item["snippet"]["language"]
    print "Caption track '%s(%s)' in '%s' language." % (name, id, language)

  return results["items"]


# Call the API's captions.insert method to upload a caption track in draft status.
def upload_caption(youtube, video_id, language, name, file):
  insert_result = youtube.captions().insert(
    part="snippet",
    body=dict(
      snippet=dict(
        videoId=video_id,
        language=language,
        name=name,
        isDraft=True
      )
    ),
    media_body=file
  ).execute()

  id = insert_result["id"]
  name = insert_result["snippet"]["name"]
  language = insert_result["snippet"]["language"]
  status = insert_result["snippet"]["status"]
  print "Uploaded caption track '%s(%s) in '%s' language, '%s' status." % (name,
      id, language, status)


# Call the API's captions.update method to update an existing caption track's draft status
# and publish it. If a new binary file is present, update the track with the file as well.
def update_caption(youtube, caption_id, file):
  update_result = youtube.captions().update(
    part="snippet",
    body=dict(
      id=caption_id,
      snippet=dict(
        isDraft=False
      )
    ),
    media_body=file
  ).execute()

  name = update_result["snippet"]["name"]
  isDraft = update_result["snippet"]["isDraft"]
  print "Updated caption track '%s' draft status to be: '%s'" % (name, isDraft)
  if file:
    print "and updated the track with the new uploaded file."


# Call the API's captions.download method to download an existing caption track.
def download_caption(youtube, caption_id, tfmt):
  subtitle = youtube.captions().download(
    id=caption_id,
    tfmt=tfmt
  ).execute()

  print "First line of caption track: %s" % (subtitle)

# Call the API's captions.delete method to delete an existing caption track.
def delete_caption(youtube, caption_id):
  youtube.captions().delete(
    id=caption_id
  ).execute()

  print "caption track '%s' deleted succesfully" % (caption_id)


if __name__ == "__main__":
  # The "videoid" option specifies the YouTube video ID that uniquely
  # identifies the video for which the caption track will be uploaded.
  argparser.add_argument("--videoid",
    help="Required; ID for video for which the caption track will be uploaded.")
  # The "name" option specifies the name of the caption trackto be used.
  argparser.add_argument("--name", help="Caption track name", default="YouTube for Developers")
  # The "file" option specifies the binary file to be uploaded as a caption track.
  argparser.add_argument("--file", help="Captions track file to upload")
  # The "language" option specifies the language of the caption track to be uploaded.
  argparser.add_argument("--language", help="Caption track language", default="en")
  # The "captionid" option specifies the ID of the caption track to be processed.
  argparser.add_argument("--captionid", help="Required; ID of the caption track to be processed")
  # The "action" option specifies the action to be processed.
  argparser.add_argument("--action", help="Action", default="all")


  args = argparser.parse_args()

  if (args.action in ('upload', 'list', 'all')):
    if not args.videoid:
          exit("Please specify videoid using the --videoid= parameter.")

  if (args.action in ('update', 'download', 'delete')):
    if not args.captionid:
          exit("Please specify captionid using the --captionid= parameter.")

  if (args.action in ('upload', 'all')):
    if not args.file:
      exit("Please specify a caption track file using the --file= parameter.")
    if not os.path.exists(args.file):
      exit("Please specify a valid file using the --file= parameter.")

  youtube = get_authenticated_service(args)
  try:
    if args.action == 'upload':
      upload_caption(youtube, args.videoid, args.language, args.name, args.file)
    elif args.action == 'list':
      list_captions(youtube, args.videoid)
    elif args.action == 'update':
      update_caption(youtube, args.captionid, args.file);
    elif args.action == 'download':
      download_caption(youtube, args.captionid, 'srt')
    elif args.action == 'delete':
      delete_caption(youtube, args.captionid);
    else:
      # All the available methods are used in sequence just for the sake of an example.
      upload_caption(youtube, args.videoid, args.language, args.name, args.file)
      captions = list_captions(youtube, args.videoid)

      if captions:
        first_caption_id = captions[0]['id'];
        update_caption(youtube, first_caption_id, None);
        download_caption(youtube, first_caption_id, 'srt')
        delete_caption(youtube, first_caption_id);
  except HttpError, e:
    print "An HTTP error %d occurred:\n%s" % (e.resp.status, e.content)
  else:
    print "Created and managed caption tracks."

Errors

The table below identifies error messages that the API could return in response to a call to this method. Please see the error message documentation for more detail.

Error type Error detail Description
forbidden (403) forbidden The permissions associated with the request are not sufficient to download the caption track. The request might not be properly authorized, or the video order might not have enabled third-party contributions for this caption.
invalidValue (400) couldNotConvert The caption track data could not be converted to the requested language and/or format. Ensure that the requested tfmt and tlang values are valid, and that the snippet.status of the requested caption track is not failed.
notFound (404) captionNotFound The caption track could not be found. Check the value of the request's id parameter to ensure that it is correct.