通过
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 名称:
|
|
可选参数 | ||
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 请求和响应。