Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

YouTube API v3 - Direct upload to own channel

as YouTube v2 API is getting deprecated I need to migrate to v3.

With v2 I used the also deprecated ClientLogin (http://bit.ly/v2_ClientLogin_authentification) authentication method to authenticate and then upload user videos to my YouTube channel.

With v3 I tried to authenticate with a service account, with the same codebase used here

PHP Youtube API v3 - Direct Upload - Unauthorized message

and I'm getting the same error message from the API endpoint:

Error calling POST https://www.googleapis.com/youtube/v3/videos?part=status%2Csnippet: (401) Unauthorized

I also checked via Google's tokeninfo (https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=[ACCESS_TOKEN]) that the access token is valid:

{
 "issued_to": "***.apps.googleusercontent.com",
 "audience": "***.apps.googleusercontent.com",
 "scope": "https://www.googleapis.com/auth/youtube",
 "expires_in": 3592,
 "access_type": "offline"
}

All YouTube examples I found are based on OAuth2 and user authorization. (like this one here: http://bit.ly/yt-api-example-videoupload). But that's not the way I want: The user shouldn't see any "Please authorize whatever.de to upload YouTube videos" AND the videos should be uploaded to my YT channel instead of theirs.

If there's no possibilty to use an API service account for that, could you please give me a hint how to do that with OAuth2? :-)

Here's my current code:

/** Config */ 
$private_key_password = 'notasecret';
$private_key_file = '***.p12';
$applicationName = '***';
$client_secret = '***'; // <--- WHERE IS THAT CLIENT_SECRET COMING FROM? Secret accounts do not have a client secret? 
$client_id = '***.apps.googleusercontent.com';
$service_mail = '***@developer.gserviceaccount.com';

$scope = 'https://www.googleapis.com/auth/youtube';
$url_youtube_token = 'https://accounts.google.com/o/oauth2/token';

$jwt = new Google_Auth_AssertionCredentials($service_mail, $scope, $private_key_file, $private_key_password, $url_youtube_token);
$jwt_assertion = $jwt->generateAssertion();

$data = array
(
    'grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
    'assertion' => $jwt_assertion,
);

$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);

//var_dump($result); die();

try{
    // Client init
    $client = new Google_Client();
    $client->setClientId($client_id);
    //$client->setClientSecret($client_secret); // <-- See above
    $client->setApplicationName($applicationName);

    $client->setAccessToken($result);

    if ($client->getAccessToken()) 
    {
        if($client->isAccessTokenExpired()) 
        {
            // @TODO Log error
            echo 'Access Token Expired!!<br/>'; // Debug
        }

        $youtube = new Google_Service_Youtube($client);

        $videoPath = "./test.mp4";

        // Create a snipet with title, description, tags and category id
        $snippet = new Google_Service_YouTube_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_Service_YouTube_VideoStatus();
        $status->privacyStatus = "public";

        // Create a YouTube video with snippet and status
        $video = new Google_Service_YouTube_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
        $insertRequest = $youtube->videos->insert("status,snippet", $video);

        $media = new Google_Http_MediaFileUpload
        (
            $client,
            $insertRequest,
            '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_Service_Exception $e)
{
    print "Caught Google service Exception ".$e->getCode(). " message is <br>".$e->getMessage(). " <br>";
    //print "Stack trace is ".$e->getTraceAsString();
}
catch (Exception $e)
{
    echo $e->getMessage();
}
?>
like image 627
Heisnberg Avatar asked Nov 09 '22 15:11

Heisnberg


1 Answers

It is not possible to access the YouTube API with a service account

Issue 5370: Youtube v3 Google Service Account Access

You will need to use Oauth2 You will only need to authenticate once. Save your refresh token using offline access. then you can use that to run your script.

$client->setAccessType('offline');   // Gets us our refreshtoken
like image 54
DaImTo Avatar answered Dec 10 '22 20:12

DaImTo