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 不包含可变值,该 part 仍会包含在 API 响应中。

以下列表包含您可以包含在参数值中的 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 内容管理系统用户。此参数适用于拥有和管理众多不同 YouTube 频道的 YouTube 内容合作伙伴。它可让内容所有者在一次身份验证后获得访问其所有视频和频道数据的权限,而无需为每个频道提供身份验证凭据。用户进行身份验证时所用的 CMS 账号必须与指定的 YouTube 内容所有者相关联。
onBehalfOfContentOwnerChannel string
此参数只能在正确授权的请求中使用。此参数只能在正确授权的请求中使用。注意:此参数仅适用于 YouTube 内容合作伙伴。

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

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

请求正文

在请求正文中提供视频资源。 针对该资源:

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

    • 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 请求和响应。