Videos: insert

凡是透過 2020 年 7 月 28 日之後建立的未驗證 API 專案,透過 videos.insert 端點上傳的所有影片,都會僅限私人觀看模式使用。如要取消這項限制,每個 API 專案都必須接受稽核,確認是否符合《服務條款》的規定。詳情請參閱 API 修訂版本記錄

將影片上傳到 YouTube 並視需要設定影片的中繼資料。

這個方法支援媒體上傳。上傳檔案必須符合下列限制:

  • 檔案大小上限:256 GB
  • 系統接受的媒體 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 CMS 使用者。這個參數適用於擁有和管理多個不同 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 要求和回應。