Reputation: 823
I'm trying to direct upload videos to Youtube with API v3.
I'm using service account scenario (https://developers.google.com/accounts/docs/OAuth2?hl=es&csw=1#scenarios), and I solved some problems in the google-api-php-client library (to read the p12 file and to avoid the isAccessTokenExpired always return false).
<?php
/** Config */
$private_key_password = 'notasecret';
$private_key_file = 'xxxxxxxx-privatekey.p12';
$applicationName = 'xxxxx-youtube';
$client_secret = 'CLIENT_SECRET';
$client_id = 'xxxxxxxxxxxxx.apps.googleusercontent.com';
$service_mail = '[email protected]';
$public_key = 'xxxxxxxxxxx';
/** Constants */
$scope = 'https://www.googleapis.com/auth/youtube';
$url_youtube_token = 'https://accounts.google.com/o/oauth2/token';
/** Create and sign JWT */
$jwt = new Google_AssertionCredentials($service_mail, $scope, $private_key_file, $private_key_password, $url_youtube_token);
$jwt_assertion = $jwt->generateAssertion();
/** Use JWT to request token */
$data = array(
'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
'assertion' => $jwt_assertion,
);
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data),
),
);
$context = stream_context_create($options);
$result = file_get_contents($url_youtube_token, false, $context);
At that point I've got the access token in a json response like this:
{
"access_token" : "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
"token_type" : "Bearer",
"expires_in" : 3600
}
without the "created", "refresh_token" and "id_token" fields. So I fixed the setAccessToken Method in Google_OAuth2 class seting the "created" field to time() if it's not set. Otherwise isAccessTokenExpired always return false.
Now, let's go to upload the file.
try{
// Client init
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setApplicationName($applicationName);
$client->setAccessToken($result);
if ($client->getAccessToken()) {
if($client->isAccessTokenExpired()) {
// @TODO Log error
echo 'Access Token Expired!!<br/>'; // Debug
}
$youtube = new Google_YoutubeService($client);
$videoPath = "./test.mp4";
// Create a snipet with title, description, tags and category id
$snippet = new Google_VideoSnippet();
$snippet->setTitle("fmgonzalez test " . time());
$snippet->setDescription("fmgonzalez test " . time() );
$snippet->setTags(array("tag1", "tag2"));
// Numeric video category. See
// https://developers.google.com/youtube/v3/docs/videoCategories/list
$snippet->setCategoryId("22");
// Create a video status with privacy status. Options are "public", "private" and "unlisted".
$status = new Google_VideoStatus();
$status->privacyStatus = "public";
// Create a YouTube video with snippet and status
$video = new Google_Video();
$video->setSnippet($snippet);
$video->setStatus($status);
// Size of each chunk of data in bytes. Setting it higher leads faster upload (less chunks,
// for reliable connections). Setting it lower leads better recovery (fine-grained chunks)
$chunkSizeBytes = 1 * 1024 * 1024;
// Create a MediaFileUpload with resumable uploads
$media = new Google_MediaFileUpload('video/*', null, true, $chunkSizeBytes);
$media->setFileSize(filesize($videoPath));
// Create a video insert request
$insertResponse = $youtube->videos->insert("status,snippet", $video,
array('mediaUpload' => $media));
$uploadStatus = false;
// Read file and upload chunk by chunk
$handle = fopen($videoPath, "rb");
$cont = 1;
while (!$uploadStatus && !feof($handle)) {
$chunk = fread($handle, $chunkSizeBytes);
$uploadStatus = $media->nextChunk($insertResponse, $chunk);
echo 'Chunk ' . $cont . ' uploaded <br/>';
$cont++;
}
fclose($handle);
echo '<br/>OK<br/>';
}else{
// @TODO Log error
echo 'Problems creating the client';
}
} catch(Google_ServiceException $e) {
print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage(). " <br>";
print "Stack trace is ".$e->getTraceAsString();
}catch (Exception $e) {
echo $e->getMessage();
}
But I receive an "Failed to start the resumable upload" message.
Debugging, the method getResumeUri in Google_MediaFileUpload I've got this response body:
"error": {
"errors": [
{
"domain": "youtube.header",
"reason": "youtubeSignupRequired",
"message": "Unauthorized",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Unauthorized"
}
I've found examples about other scenarios but not about this one.
What should I do to finally upload the video file? Any example about this scenario?
Thanks in advance.
Upvotes: 4
Views: 4236
Reputation: 81
The error response you're getting is by design. Google does not allow Youtube API access via service accounts.
Service accounts do not work for YouTube Data API calls because service accounts require an associated YouTube channel, and you cannot associate new or existing channels with service accounts. If you use a service account to call the YouTube Data API, the API server returns an error with the error type set to unauthorized and the reason set to youtubeSignupRequired.
Upvotes: 2
Reputation: 367
It may looks trivial but did you created at least one channel in the account you want to upload the video to. I used almost the same workaround as you for the acceess_token then encountered the same problem until I went in my Youtube account upload section and saw the message create at least one channel before uploading videos. Hope it helps.
Upvotes: 4