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

回應

如果成功的話,這個方法會在回應主體中傳回影片資源

範例

注意:下列程式碼範例可能不是所有支援的程式設計語言。如需支援的語言清單,請參閱用戶端程式庫說明文件。

查看

這個程式碼範例會呼叫 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 要求和回應。