Videos: insert

对于在 2020 年 7 月 28 日之后创建的未经验证的 API 项目,通过 videos.insert 端点上传的所有视频将仅限私享观看模式。要解除此限制,每个 API 项目都必须接受审核,以验证是否符合服务条款。如需了解详情,请参阅 API 修订历史记录

将视频上传到 YouTube,并视需要设置视频的元数据。

此方法支持媒体上传。上传的文件必须符合以下限制:

  • 文件大小上限:256GB
  • 接受的媒体 MIME 类型video/*application/octet-stream

配额影响:调用此方法的配额费用为 1600 个单位。

常见使用场景

请求

HTTP 请求

POST https://www.googleapis.com/upload/youtube/v3/videos

授权

此请求需要获得以下至少一个范围的授权(详细了解身份验证和授权)。

范围
https://www.googleapis.com/auth/youtube.upload
https://www.googleapis.com/auth/youtube
https://www.googleapis.com/auth/youtubepartner
https://www.googleapis.com/auth/youtube.force-ssl

参数

下表列出了此查询支持的参数。列出的所有参数都是查询参数。

参数
必需参数
part string
part 参数在此操作中有两个目的。它用于标识写入操作将设置的属性以及 API 响应将包含的属性。

请注意,并非所有部分都包含可在插入或更新视频时设置的属性。例如,statistics 对象封装了 YouTube 为视频计算的统计信息,但不包含您可设置或修改的值。如果参数值指定的 part 不包含可变值,该 API 响应中仍会包含 part

以下列表包含您可以在参数值中包含的 part 名称:
  • contentDetails
  • fileDetails
  • id
  • liveStreamingDetails
  • localizations
  • player
  • processingDetails
  • recordingDetails
  • snippet
  • statistics
  • status
  • suggestions
  • topicDetails
可选参数
notifySubscribers boolean
notifySubscribers 参数用于指明 YouTube 是否应向订阅该频道的用户发送关于新视频的通知。参数值为 True 表示会向订阅者发送有关新上传的视频的通知。但是,如果频道所有者要上传多个视频,可能更希望将该值设置为 False,以避免向该频道的订阅者发送有关每个新视频的通知。默认值为 True
onBehalfOfContentOwner string
此参数只能在正确的授权请求中使用。注意:此参数专供 YouTube 内容合作伙伴使用。

onBehalfOfContentOwner 参数表示请求的授权凭据用于标识代表参数值中指定的内容所有者执行操作的 YouTube CMS 用户。此参数适用于拥有和管理许多不同 YouTube 频道的 YouTube 内容合作伙伴。内容所有者只需进行一次身份验证,即可访问自己所有的视频和频道数据,而无需为每个频道提供身份验证凭据。用户身份验证所用的 CMS 帐号必须与指定的 YouTube 内容所有者相关联。
onBehalfOfContentOwnerChannel string
此参数只能在正确的授权请求中使用。此参数只能在适当的授权请求中使用。注意:此参数仅适用于 YouTube 内容合作伙伴。

onBehalfOfContentOwnerChannel 参数用于指定要添加视频的频道的 YouTube 频道 ID。当请求为 onBehalfOfContentOwner 参数指定值时,必须提供此参数,并且该参数只能与该参数结合使用。此外,必须使用与 onBehalfOfContentOwner 参数指定的内容所有者相关联的 CMS 帐号对请求进行授权。最后,onBehalfOfContentOwnerChannel 参数值指定的频道必须与 onBehalfOfContentOwner 参数指定的内容所有者相关联。

此参数适用于拥有和管理许多不同 YouTube 频道的 YouTube 内容合作伙伴。使用 Content ID,内容所有者只需进行一次身份验证,即可代表参数值中指定的频道执行操作,而无需为每个单独的频道提供身份验证凭据。

请求正文

在请求正文中提供视频资源。对于相应资源:

  • 您可以为以下属性设置值:

    • snippet.title
    • snippet.description
    • snippet.tags[]
    • snippet.categoryId
    • snippet.defaultLanguage
    • localizations.(key)
    • localizations.(key).title
    • localizations.(key).description
    • status.embeddable
    • status.license
    • status.privacyStatus
    • status.publicStatsViewable
    • status.publishAt
    • status.selfDeclaredMadeForKids
    • recordingDetails.locationDescription已弃用
    • recordingDetails.location.latitude已弃用
    • recordingDetails.location.longitude已弃用
    • recordingDetails.recordingDate

响应

如果成功,此方法将在响应正文中返回视频资源

示例

注意:以下代码示例可能并不代表所有受支持的编程语言。如需查看支持的语言列表,请参阅客户端库文档。

Go

此代码示例调用 API 的 videos.insert 方法,将视频上传到与请求关联的频道。

此示例使用 Go 客户端库

package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"

	"google.golang.org/api/youtube/v3"
)

var (
	filename    = flag.String("filename", "", "Name of video file to upload")
	title       = flag.String("title", "Test Title", "Video title")
	description = flag.String("description", "Test Description", "Video description")
	category    = flag.String("category", "22", "Video category")
	keywords    = flag.String("keywords", "", "Comma separated list of video keywords")
	privacy     = flag.String("privacy", "unlisted", "Video privacy status")
)

func main() {
	flag.Parse()

	if *filename == "" {
		log.Fatalf("You must provide a filename of a video file to upload")
	}

	client := getClient(youtube.YoutubeUploadScope)

	service, err := youtube.New(client)
	if err != nil {
		log.Fatalf("Error creating YouTube client: %v", err)
	}

	upload := &youtube.Video{
		Snippet: &youtube.VideoSnippet{
			Title:       *title,
			Description: *description,
			CategoryId:  *category,
		},
		Status: &youtube.VideoStatus{PrivacyStatus: *privacy},
	}

	// The API returns a 400 Bad Request response if tags is an empty string.
	if strings.Trim(*keywords, "") != "" {
		upload.Snippet.Tags = strings.Split(*keywords, ",")
	}

	call := service.Videos.Insert("snippet,status", upload)

	file, err := os.Open(*filename)
	defer file.Close()
	if err != nil {
		log.Fatalf("Error opening %v: %v", *filename, err)
	}

	response, err := call.Media(file).Do()
	handleError(err, "")
	fmt.Printf("Upload successful! Video ID: %v\n", response.Id)
}

.NET

以下代码示例调用该 API 的 videos.insert 方法,将视频上传到与请求关联的频道。

此示例使用 .NET 客户端库

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Upload;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;

namespace Google.Apis.YouTube.Samples
{
  /// <summary>
  /// YouTube Data API v3 sample: upload a video.
  /// Relies on the Google APIs Client Library for .NET, v1.7.0 or higher.
  /// See https://developers.google.com/api-client-library/dotnet/get_started
  /// </summary>
  internal class UploadVideo
  {
    [STAThread]
    static void Main(string[] args)
    {
      Console.WriteLine("YouTube Data API: Upload Video");
      Console.WriteLine("==============================");

      try
      {
        new UploadVideo().Run().Wait();
      }
      catch (AggregateException ex)
      {
        foreach (var e in ex.InnerExceptions)
        {
          Console.WriteLine("Error: " + e.Message);
        }
      }

      Console.WriteLine("Press any key to continue...");
      Console.ReadKey();
    }

    private async Task Run()
    {
      UserCredential credential;
      using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
      {
        credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
            GoogleClientSecrets.Load(stream).Secrets,
            // This OAuth 2.0 access scope allows an application to upload files to the
            // authenticated user's YouTube channel, but doesn't allow other types of access.
            new[] { YouTubeService.Scope.YoutubeUpload },
            "user",
            CancellationToken.None
        );
      }

      var youtubeService = new YouTubeService(new BaseClientService.Initializer()
      {
        HttpClientInitializer = credential,
        ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
      });

      var video = new Video();
      video.Snippet = new VideoSnippet();
      video.Snippet.Title = "Default Video Title";
      video.Snippet.Description = "Default Video Description";
      video.Snippet.Tags = new string[] { "tag1", "tag2" };
      video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
      video.Status = new VideoStatus();
      video.Status.PrivacyStatus = "unlisted"; // or "private" or "public"
      var filePath = @"REPLACE_ME.mp4"; // Replace with path to actual movie file.

      using (var fileStream = new FileStream(filePath, FileMode.Open))
      {
        var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
        videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
        videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;

        await videosInsertRequest.UploadAsync();
      }
    }

    void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
    {
      switch (progress.Status)
      {
        case UploadStatus.Uploading:
          Console.WriteLine("{0} bytes sent.", progress.BytesSent);
          break;

        case UploadStatus.Failed:
          Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
          break;
      }
    }

    void videosInsertRequest_ResponseReceived(Video video)
    {
      Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
    }
  }
}

Ruby

此示例调用 API 的 videos.insert 方法,将视频上传到与请求关联的频道。

本示例使用 Ruby 客户端库

#!/usr/bin/ruby

require 'rubygems'
gem 'google-api-client', '>0.7'
require 'google/api_client'
require 'google/api_client/client_secrets'
require 'google/api_client/auth/file_storage'
require 'google/api_client/auth/installed_app'
require 'trollop'

# A limited OAuth 2 access scope that allows for uploading files, but not other
# types of account access.
YOUTUBE_UPLOAD_SCOPE = 'https://www.googleapis.com/auth/youtube.upload'
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

def get_authenticated_service
  client = Google::APIClient.new(
    :application_name => $PROGRAM_NAME,
    :application_version => '1.0.0'
  )
  youtube = client.discovered_api(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION)

  file_storage = Google::APIClient::FileStorage.new("#{$PROGRAM_NAME}-oauth2.json")
  if file_storage.authorization.nil?
    client_secrets = Google::APIClient::ClientSecrets.load
    flow = Google::APIClient::InstalledAppFlow.new(
      :client_id => client_secrets.client_id,
      :client_secret => client_secrets.client_secret,
      :scope => [YOUTUBE_UPLOAD_SCOPE]
    )
    client.authorization = flow.authorize(file_storage)
  else
    client.authorization = file_storage.authorization
  end

  return client, youtube
end

def main
  opts = Trollop::options do
    opt :file, 'Video file to upload', :type => String
    opt :title, 'Video title', :default => 'Test Title', :type => String
    opt :description, 'Video description',
          :default => 'Test Description', :type => String
    opt :category_id, 'Numeric video category. See https://developers.google.com/youtube/v3/docs/videoCategories/list',
          :default => 22, :type => :int
    opt :keywords, 'Video keywords, comma-separated',
          :default => '', :type => String
    opt :privacy_status, 'Video privacy status: public, private, or unlisted',
          :default => 'public', :type => String
  end

  if opts[:file].nil? or not File.file?(opts[:file])
    Trollop::die :file, 'does not exist'
  end

  client, youtube = get_authenticated_service

  begin
    body = {
      :snippet => {
        :title => opts[:title],
        :description => opts[:description],
        :tags => opts[:keywords].split(','),
        :categoryId => opts[:category_id],
      },
      :status => {
        :privacyStatus => opts[:privacy_status]
      }
    }

    videos_insert_response = client.execute!(
      :api_method => youtube.videos.insert,
      :body_object => body,
      :media => Google::APIClient::UploadIO.new(opts[:file], 'video/*'),
      :parameters => {
        :uploadType => 'resumable',
        :part => body.keys.join(',')
      }
    )

    videos_insert_response.resumable_upload.send_all(client)

    puts "Video id '#{videos_insert_response.data.id}' was successfully uploaded."
  rescue Google::APIClient::TransmissionError => e
    puts e.result.body
  end
end

main

错误

下表列出了该 API 为响应对此方法的调用可能返回的错误消息。如需了解详情,请参阅错误消息文档。

错误类型 错误详情 说明
badRequest (400) defaultLanguageNotSet 该请求正尝试添加本地化视频详细信息,而不指定视频详细信息的默认语言。
badRequest (400) invalidCategoryId snippet.categoryId 属性指定的类别 ID 无效。使用 videoCategories.list 方法检索支持的类别。
badRequest (400) invalidDescription 请求元数据指定的视频说明无效。
badRequest (400) invalidFilename Slug 标头中指定的视频文件名无效。
badRequest (400) invalidPublishAt 请求元数据指定了无效的计划发布时间。
badRequest (400) invalidRecordingDetails 请求元数据中的 recordingDetails 对象指定无效的录制详情。
badRequest (400) invalidTags 请求元数据指定了无效的视频关键字。
badRequest (400) invalidTitle 请求元数据指定的视频标题无效或为空。
badRequest (400) invalidVideoGameRating 请求元数据指定的视频游戏分级无效。
badRequest (400) invalidVideoMetadata 请求元数据无效。
badRequest (400) mediaBodyRequired 此请求不包含视频内容。
badRequest (400) uploadLimitExceeded 用户上传的视频数量已超过上限。
forbidden (403) forbidden
forbidden (403) forbiddenLicenseSetting 该请求尝试为视频设置无效许可。
forbidden (403) forbiddenPrivacySetting 该请求尝试为视频设置无效的隐私设置。

试试看!

使用 APIs Explorer 调用此 API,并查看 API 请求和响应。