Search code examples
youtube-apiyoutube-data-api

use YouTube Data API (v3) to update a video via API keys got a 401 error


In my java back-end app ,I want to use quartz job to change my youtube videos status from public to private every week, it is a scheduled job. So I use YouTube Data API (v3)'s Videos Update to do this work.

See YouTube Data API Reference Videos: update and Code Samples Resource>videos,Method>update. According to Obtaining authorization credentials,there are two ways to obtain authorization credentials ,one is OAuth 2.0 the other is using API Keys.I choose to use API Keys because it is simpler than oauth2.

I have already retrieved API Keys from Google API Console , I run the code samples copied from youtube's documentation Resource>videos,Method>update and run them ,they both got 401 error.

{
  "error": {
    "code": 401,
    "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "errors": [
      {
        "message": "Login Required.",
        "domain": "global",
        "reason": "required",
        "location": "Authorization",
        "locationType": "header"
      }
    ],
    "status": "UNAUTHENTICATED"
  }
}

I do not why i can not work by using API Keys to call the videos.update. I dont want to use OAuth2, I think use API Keys is the better way.Because, it is impossible to open a browser window and make the user to do a oauth login and authorization in a quartz job, can anybody tell me what is the problem and how to do?

the code samples are as below

Java Based

/**
 * Sample Java code for youtube.videos.update
 * See instructions for running these code samples locally:
 * https://developers.google.com/explorer-help/guides/code_samples#java
 */

import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;

import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.model.Video;
import com.google.api.services.youtube.model.VideoLocalization;
import com.google.api.services.youtube.model.VideoSnippet;
import com.google.api.services.youtube.model.VideoStatus;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;

public class ApiExample {
    // You need to set this value for your code to compile.
    // For example: ... DEVELOPER_KEY = "YOUR ACTUAL KEY";
    private static final String DEVELOPER_KEY = "...";

    private static final String APPLICATION_NAME = "API code samples";
    private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();

    /**
     * Build and return an authorized API client service.
     *
     * @return an authorized API client service
     * @throws GeneralSecurityException, IOException
     */
    public static YouTube getService() throws GeneralSecurityException, IOException {
        final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
        return new YouTube.Builder(httpTransport, JSON_FACTORY, null)
            .setApplicationName(APPLICATION_NAME)
            .build();
    }

    /**
     * Call function to create API service object. Define and
     * execute API request. Print API response.
     *
     * @throws GeneralSecurityException, IOException, GoogleJsonResponseException
     */
    public static void main(String[] args)
        throws GeneralSecurityException, IOException, GoogleJsonResponseException {
        YouTube youtubeService = getService();
        
        // Define the Video object, which will be uploaded as the request body.
        Video video = new Video();
        
        // Add the id string property to the Video object.
        video.setId("9BByHcBGMP4");
        
        // Add the localizations object property to the Video object.
        HashMap<String, VideoLocalization> localizations = new HashMap<>();
        
        VideoLocalization esLocalization = new VideoLocalization();
        esLocalization.setDescription("Esta descripcion es en español.");
        esLocalization.setTitle("no hay nada a ver aqui");
        localizations.put("es", esLocalization);
        
        video.setLocalizations(localizations);
        
        // Add the snippet object property to the Video object.
        VideoSnippet snippet = new VideoSnippet();
        snippet.setCategoryId("22");
        snippet.setDefaultLanguage("en");
        snippet.setDescription("This description is in English.");
        String[] tags = {
            "new tags",
        };
        snippet.setTags(Arrays.asList(tags));
        snippet.setTitle("There is nothing to see here.");
        video.setSnippet(snippet);
        
        // Add the status object property to the Video object.
        VideoStatus status = new VideoStatus();
        status.setPrivacyStatus("private");
        video.setStatus(status);

        // Define and execute the API request
        YouTube.Videos.Update request = youtubeService.videos()
            .update("snippet,status,localizations", video);
        Video response = request.setKey(DEVELOPER_KEY).execute();
        System.out.println(response);
    }
}

Javascript Based

<!doctype html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <meta name="Generator" content="EditPlus®">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
  <title>Document</title>
 </head>
 <body>
  <script src="https://apis.google.com/js/api.js"></script>
<script>
  /**
   * Sample JavaScript code for youtube.videos.update
   * See instructions for running APIs Explorer code samples locally:
   * https://developers.google.com/explorer-help/guides/code_samples#javascript
   */

  function loadClient() {
    gapi.client.setApiKey("...");
    return gapi.client.load("https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest")
        .then(function() { console.log("GAPI client loaded for API"); },
              function(err) { console.error("Error loading GAPI client for API", err); });
  }
  // Make sure the client is loaded before calling this method.
  function execute() {
    return gapi.client.youtube.videos.update({
      "part": [
        "snippet,status,localizations"
      ],
      "resource": {
        "id": "9BByHcBGMP4",
        "snippet": {
          "categoryId": "22",
          "defaultLanguage": "en",
          "description": "This description is in English.",
          "tags": [
            "new tags"
          ],
          "title": "There is nothing to see here."
        },
        "status": {
          "privacyStatus": "private"
        },
        "localizations": {
          "es": {
            "title": "no hay nada a ver aqui",
            "description": "Esta descripcion es en español."
          }
        }
      }
    })
        .then(function(response) {
                // Handle the results here (response.result has the parsed body).
                console.log("Response", response);
              },
              function(err) { console.error("Execute error", err); });
  }
  gapi.load("client");
</script>
<button onclick="loadClient()">load</button>
<button onclick="execute()">execute</button>

 </body>
</html>


Solution

  • According to the official doc you yourself quoted, for to invoke the Videos.update API endpoint, you need to be properly authorized:

    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/youtubepartner
    https://www.googleapis.com/auth/youtube
    https://www.googleapis.com/auth/youtube.force-ssl

    Consequently, there's no way you can avoid using OAuth 2.0 authentication/authorization flow within you app. Note that API keys are used and useful for reading-only public data.


    For the part of your question that says:

    [...] it is impossible to open a browser window and make the user to do a oauth login and authorization in a quartz job [...]

    the API has solutions. Do read the following two docs: OAuth 2.0 for Mobile & Desktop Apps and Using OAuth 2.0 for Web Server Applications.

    A brief, top-level description of what you'll have to do is provided by the answer I gave recently to a similar question. That answer may help you understand more easily the way to attain a solution to your problem.