2

YouTube API v3 的文档非常糟糕。我已经多次报告了许多错误,但没有人做出反应。我仍然必须使用这个 API 来上传缩略图。该指南指出:

发布https://www.googleapis.com/youtube/v3/thumbnails/set

授权范围:

参数:

  • videoId: string videoId 参数指定为其提供自定义视频缩略图的 YouTube 视频 ID。

首先 -网址是错误的。它必须是https://www.googleapis.com/upload/youtube/v3/thumbnails/set。现在下面的代码,它使用Unirest

final HttpResponse<String> response = Unirest.post("https://www.googleapis.com/upload/youtube/v3/thumbnails/set")
                    .header("Content-Type", "application/octet-stream")
                    .header("Authorization", accountService.getAuthentication(account).getHeader())
                    .field("videoId", videoid)
                    .field("thumbnail", thumbnail)
                    .asString();

收到的回复:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Required parameter: videoId",
    "locationType": "parameter",
    "location": "videoId"
   }
  ],
  "code": 400,
  "message": "Required parameter: videoId"
 }
}

怎么会这样?videoId 已设置!有人已经玩过这部分 API 了吗?

我可以将请求更改为

Unirest.post("https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=" + videoid)
                    .header("Content-Type", "application/octet-stream")
                    .header("Authorization", accountService.getAuthentication(account).getHeader())
                    .field("mediaUpload", thumbnail)
                    .asString();

这会给我这个错误:

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "backendError",
    "message": "Backend Error"
   }
  ],
  "code": 503,
  "message": "Backend Error"
 }
}

编辑:与 Ibrahim Ulukaya 发布的 URL 相同的请求(参考指南中的原始 url):

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "wrongUrlForUpload",
    "message": "Uploads must be sent to the upload URL. Re-send this request to https://www.googleapis.com/upload/youtube/v3/thumbnails/set"
   }
  ],
  "code": 400,
  "message": "Uploads must be sent to the upload URL. Re-send this request to https://www.googleapis.com/upload/youtube/v3/thumbnails/set"
 }
}
4

3 回答 3

4

我们挖掘了这个问题,如果您不想使用该库,请按照以下步骤操作。

1) POST https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=VIDEO_ID&uploadType=resumable 正文为空

2) 取回 Location: 响应头中的 URL,并使用 Content-Type: image/png 和正文中的缩略图 POST 到该 URL

于 2013-09-06T21:12:14.867 回答
1

URL will be fixed.

You also need to have specific permission in your channel to set custom thumbnails.

There is PHP and Python examples in our sample code that works.

Here's the Java one, I just wrote and tested, it works.

/*
 * Copyright (c) 2013 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.youtube_cmdline_uploadthumbnail_sample;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.java6.auth.oauth2.FileCredentialStore;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.googleapis.media.MediaHttpUploader;
import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.InputStreamContent;
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.YouTube.Thumbnails.Set;
import com.google.api.services.youtube.model.ThumbnailSetResponse;
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.IOException;
import java.io.InputStreamReader;
import java.util.List;

/**
 * This sample uploads and sets a custom thumbnail for a video by:
 *
 *  1. Uploading a image utilizing "MediaHttpUploader" 2. Setting the uploaded image as a custom
 * thumbnail to the video via "youtube.thumbnails.set" method
 *
 * @author Ibrahim Ulukaya
 */
public class UploadThumbnail {

  /**
   * Global instance of the HTTP transport.
   */
  private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();

  /**
   * Global instance of the JSON factory.
   */
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();

  /**
   * Global instance of Youtube object to make all API requests.
   */
  private static YouTube youtube;

  /* Global instance of the format used for the image being uploaded (MIME type). */
  private static String IMAGE_FILE_FORMAT = "image/png";



  /**
   * Authorizes the installed application to access user's protected data.
   *
   * @param scopes list of scopes needed to run youtube upload.
   */
  private static Credential authorize(List<String> scopes) throws IOException {

    // Load client secrets.
    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
        new InputStreamReader(UploadThumbnail.class.getResourceAsStream("/client_secrets.json")));

    // Checks that the defaults have been replaced (Default = "Enter X here").
    if (clientSecrets.getDetails().getClientId().startsWith("Enter")
        || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
      System.out.println(
          "Enter Client ID and Secret from https://code.google.com/apis/console/?api=youtube"
          + "into youtube-cmdline-uploadthumbnail-sample/src/main/resources/client_secrets.json");
      System.exit(1);
    }

    // Set up file credential store.
    FileCredentialStore credentialStore = new FileCredentialStore(
        new File(System.getProperty("user.home"), ".credentials/youtube-api-uploadthumbnail.json"),
        JSON_FACTORY);

    // Set up authorization code flow.
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, scopes).setCredentialStore(credentialStore)
        .build();

    // Build the local server and bind it to port 8080
    LocalServerReceiver localReceiver = new LocalServerReceiver.Builder().setPort(8080).build();

    // Authorize.
    return new AuthorizationCodeInstalledApp(flow, localReceiver).authorize("user");
  }

  /**
   * This is a very simple code sample that looks up a user's channel, then features the most
   * recently uploaded video in the bottom left hand corner of every single video in the channel.
   *
   * @param args command line args (not used).
   */
  public static void main(String[] args) {

    // An OAuth 2 access scope that allows for full read/write access.
    List<String> scopes = Lists.newArrayList("https://www.googleapis.com/auth/youtube");

    try {
      // Authorization.
      Credential credential = authorize(scopes);

      // YouTube object used to make all API requests.
      youtube = new YouTube.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).setApplicationName(
          "youtube-cmdline-addfeaturedvideo-sample").build();

      // Get the user selected video Id.
      String videoId = getVideoIdFromUser();
      System.out.println("You chose " + videoId + " to upload a thumbnail.");

      // Get the user selected local image file to upload.
      File imageFile = getImageFromUser();
      System.out.println("You chose " + imageFile + " to upload.");

      InputStreamContent mediaContent = new InputStreamContent(
          IMAGE_FILE_FORMAT, new BufferedInputStream(new FileInputStream(imageFile)));
      mediaContent.setLength(imageFile.length());

      // Create a request to set the selected mediaContent as the thumbnail of the selected video.
      Set thumbnailSet = youtube.thumbnails().set(videoId, mediaContent);

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

      /*
       * Sets whether direct media upload is enabled or disabled. True = whole media content is
       * uploaded in a single request. False (default) = resumable media upload protocol to upload
       * in data chunks.
       */
      uploader.setDirectUploadEnabled(false);

      MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
        @Override
        public void progressChanged(MediaHttpUploader uploader) throws IOException {
          switch (uploader.getUploadState()) {
            case INITIATION_STARTED:
              System.out.println("Initiation Started");
              break;
            case INITIATION_COMPLETE:
              System.out.println("Initiation Completed");
              break;
            case MEDIA_IN_PROGRESS:
              System.out.println("Upload in progress");
              System.out.println("Upload percentage: " + uploader.getProgress());
              break;
            case MEDIA_COMPLETE:
              System.out.println("Upload Completed!");
              break;
            case NOT_STARTED:
              System.out.println("Upload Not Started!");
              break;
          }
        }
      };
      uploader.setProgressListener(progressListener);

      // Execute upload and set thumbnail.
      ThumbnailSetResponse setResponse = thumbnailSet.execute();

      // Print out returned results.
      System.out.println("\n================== Uploaded Thumbnail ==================\n");
      System.out.println("  - Url: " + setResponse.getItems().get(0).getDefault().getUrl());

    } 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();
    }
  }

  /*
   * Prompts for a video ID from standard input and returns it.
   */
  private static String getVideoIdFromUser() throws IOException {

    String title = "";

    System.out.print("Please enter a video Id to update: ");
    BufferedReader bReader = new BufferedReader(new InputStreamReader(System.in));
    title = bReader.readLine();

    if (title.length() < 1) {
      // If nothing is entered, exits
      System.out.print("Video Id can't be empty!");
      System.exit(1);
    }

    return title;
  }

  /*
   * Prompts for the path of the image file to upload from standard input and returns it.
   */
  private static File getImageFromUser() throws IOException {

    String path = "";

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

    if (path.length() < 1) {
      // If nothing is entered, exits
      System.out.print("Path can not be empty!");
      System.exit(1);
    }

    return new File(path);
  }
}
于 2013-08-22T14:08:43.043 回答
0

给出的答案甚至不接近正确!如果您在正文中没有图片的情况下发布到此 URL:

https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=VIDEO_ID&uploadType=resumable

您将收到此错误:

“400:mediaBodyRequired”

此错误在本页底部的 YouTube 文档中进行了描述:

https://developers.google.com/youtube/v3/docs/thumbnails/set

作为:

“请求不包括图像内容。”

尽管他们的文档遗漏了很多东西(想想 categoryId),但他们已经死在这里了,因为我刚刚尝试了 Ibrahim 发布的第一个解决方案并收到了错误。尽管他声明不提供正文中的图像数据,但文档和我自己的研究表明完全相反。

解决方案是发布到包含图像正文的此 URL。这样做时,我收到了以下回复:

{“种类”:“youtube#thumbnailSetResponse”,“etag”:“\”kYnGHzMaBhcGeLrcKRx6PAIUosY/lcDPfygjJkG-yyzdBp0dKhY2xMY\“”,“项目”:[{“默认”:{“url”:“//i.ytimg.com /vi/fyBx3v1gmbM/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "//i.ytimg.com/vi/fyBx3v1gmbM/mqdefault.jpg", “宽度”:320,“高度”:180 },“高度”:{ “url”:“//i.ytimg.com/vi/fyBx3v1gmbM/hqdefault.jpg”,“宽度”:480,“高度”: 360 },“标准”:{“url”:“//i.ytimg.com/vi/fyBx3v1gmbM/sddefault.jpg”,“宽度”:640,“高度”:480 },“maxres”:{“url ": "//i.ytimg.com/vi/fyBx3v1gmbM/maxresdefault.jpg”,“宽度”:1280,“高度”:720 } } ] }

不幸的是,它实际上并没有改变缩略图,但我认为这是他们的 API 服务器和他们的缩略图处理排队的问题。它确实返回了成功的响应。不知道为什么缩略图没有改变。

于 2015-04-15T23:05:28.117 回答