rubyyoutube-data-apigoogle-api-ruby-client

YouTube Data API v3 update_video fails with ArgumentError


Problem

Using the google-apis-youtube_v3 gem I can upload a video, delete a video, insert captions, delete captions, and set a thumbnail image. The only thing that that for some reason does not work is the update_video call, which always returns an error.

Environment

Steps to reproduce

  1. Instantiate service object and call update_video method with part = 'id,snippet,status' and a video object specifying the video ID, snippet and status, following instructions in the method documentation.

Code example

require 'googleauth'
require 'googleauth/stores/file_token_store'
require 'google-apis-youtube_v3'

REDIRECT_URI = 'http://localhost'
APPLICATION_NAME = '...'
API_KEY = '...'

SCOPE = Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL

def authorize
  client_id = Google::Auth::ClientId.from_file('secrets/client_secret.json')
  token_store = Google::Auth::Stores::FileTokenStore.new(file: "secrets/youtube-creds.yml")
  authorizer = Google::Auth::UserAuthorizer.new(client_id, SCOPE, token_store)
  user_id = 'default'
  credentials = authorizer.get_credentials(user_id)
  if credentials.nil?
    url = authorizer.get_authorization_url(base_url: REDIRECT_URI)
    puts "Open the following URL in the browser and enter the resulting code after authorization"
    puts url
    code = gets
    credentials = authorizer.get_and_store_credentials_from_code(user_id: user_id, code: code, base_url: REDIRECT_URI)
  end
  credentials
end

def get_service
  service = Google::Apis::YoutubeV3::YouTubeService.new
  service.key = API_KEY
  service.client_options.application_name = APPLICATION_NAME
  service.authorization = authorize
  service
end

o = {
  "id": 'XYZ123',
  "snippet": {
    "category_id": "27",
    "default_language": "en",
    "description": "Testing updating description and title.",
    "tags": [
      "tag1",
      "tag2",
      "tag3"
    ],
    "title": "Test video upload using Ruby SDK"
  },
  "status": {
    "privacy_status": "private"
  }
}
resp = s.update_video('id,snippet,status', o)

Result

ArgumentError (unknown keywords: :id, :snippet, :status)

Note that the video object is almost identical to the one used to create the video in the first place. The only change, apart from trivial modifications of the video title and description, was the addition of the id attribute to identify the video.


Solution

  • Jim Gay solved the problem in his comment. The ArgumentError is not, as I thought, thrown by the YouTube API gem. It's a Ruby exception thrown before the API method can be called.

    The problem is that the second parameter to the method defaults to nil, so if you pass in the video object as a plain hash and it happens to be both the second and the last parameter in the method call, it will be interpreted by Ruby as a collection of keyword (named) parameters and not as the single value to be passed to the second argument. So :id, :snippet and :status are not recognized as valid parameters since the method signature does not declare them.

    This was not a problem with the insert_video API call, since there in addition to the video object you most likely have to pass in the location of the video file as one of the named parameters.

    As Jim indicated, you can construct a Google::Apis::YoutubeV3::Video object and pass that in as the second/last argument with no ambiguity. The only inconvenience there is that you would also have to construct the video snippet and status objects inside the video object.

    Another possibility is first to fetch the video object from YouTube using the list_videos API call, edit the values as needed, and then pass it to update_video.

    The simplest thing, though, is just to pass the video object as a plain hash, but also pass in an actual named parameter to let Ruby know that the second argument is not supposed to be unpacked before passing it in. For example:

    resp = s.update_video('id,snippet,status', o, options: nil)