Videos: insert

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

将视频上传到 YouTube,并可选择设置视频的元数据。

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

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

配额影响:调用此方法将产生 1,600 个单元的配额费用

常见使用场景

请求

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
  • paidProductPlacementDetails
  • player
  • processingDetails
  • recordingDetails
  • snippet
  • statistics
  • status
  • suggestions
  • topicDetails
可选参数
notifySubscribers boolean
notifySubscribers 参数用于指明 YouTube 是否应向订阅视频频道的用户发送有关新视频的通知。参数值 True 表示订阅者将收到有关新上传视频的通知。不过,如果频道所有者上传的视频很多,则可能更倾向于将此值设为 False,以免向频道的订阅者发送有关每个新视频的通知。默认值为 True
onBehalfOfContentOwner string
此参数只能在适当授权的请求中使用。注意:此参数仅供 YouTube 内容合作伙伴使用。

onBehalfOfContentOwner 参数表示请求的授权凭据标识了代表参数值中指定的内容所有者行事的 YouTube CMS 用户。此参数适用于拥有和管理许多不同 YouTube 频道的 YouTube 内容合作伙伴。借助此功能,内容所有者只需进行一次身份验证,即可访问其所有视频和频道数据,而无需为每个频道分别提供身份验证凭据。用户进行身份验证时使用的内容管理系统账号必须与指定的 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
    • status.containsSyntheticMedia
    • 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 请求和响应。