Found this useful? Love this post
322

PHP server-side YouTube V3 OAuth API video upload guide

Dec 2014 – Please note this post is based on the 1.0.5 version of the API. If you are using the latest version you’ll need to update file paths as the file/folder structure has changed significantly.

Many bothans died to bring you this information. And when I say bothans, I refer of course to brain cells that have died as result of me smashing my head against the YouTube api.

To make this work, you’ll need to create a dummy web application that can capture the refresh token generated when you authorise your app. You can read a detailed explanation of what we’ll be doing and how the OAuth process works here.

This implementation has no requirements for any PHP frameworks (such as Zend). It is a barebones implementation based off the documentation for the YouTube V3 API utilising the Google PHP API client.

Creating your project

  1. You’ll need to head over to https://console.developers.google.com/ and create a new project.
  2. Then go and enable the YouTube Data API – (Your Project > APIS & AUTH > APIs)
  3. We’ll now need to create some credentials. Go to Your Project > APIS & AUTH > Credentials
  4. Click “Create new Client ID” and then under “Application Type” select “Web Application”.
  5. You’ll then need to enter your JavaScript origin and authorised redirect URI. I just set this to localhost, but you can set it to where ever you want.
  6. We’ve now created our client ID, we just need to fill in the Consent Screen. We can do this by navigating to Your Project > APIS & AUTH > Consent Screen. Once you’re on this page, ensure you fill out all the required fields (Email Address and Product Name).
  7. Hurray, we’ve created our project.

Getting your refresh token

I used the following PHP script as my dummy application to generate the OAuth token.

This script utilises Google APIs Client Library for PHP. You’ll need to download and install it a directory that can be read using the below script.

<?php

// Call set_include_path() as needed to point to your client library.
set_include_path($_SERVER['DOCUMENT_ROOT'] . '/directory/to/google/api/');
require_once 'Google/Client.php';
require_once 'Google/Service/YouTube.php';
session_start();

/*
 * You can acquire an OAuth 2.0 client ID and client secret from the
 * {{ Google Cloud Console }} <{{ https://cloud.google.com/console }}>
 * For more information about using OAuth 2.0 to access Google APIs, please see:
 * <https://developers.google.com/youtube/v3/guides/authentication>
 * Please ensure that you have enabled the YouTube Data API for your project.
 */
$OAUTH2_CLIENT_ID = 'XXXXXXX.apps.googleusercontent.com';
$OAUTH2_CLIENT_SECRET = 'XXXXXXXXXX';
$REDIRECT = 'http://localhost/oauth2callback.php';
$APPNAME = "XXXXXXXXX";


$client = new Google_Client();
$client->setClientId($OAUTH2_CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$client->setScopes('https://www.googleapis.com/auth/youtube');
$client->setRedirectUri($REDIRECT);
$client->setApplicationName($APPNAME);
$client->setAccessType('offline');


// Define an object that will be used to make all API requests.
$youtube = new Google_Service_YouTube($client);

if (isset($_GET['code'])) {
    if (strval($_SESSION['state']) !== strval($_GET['state'])) {
        die('The session state did not match.');
    }

    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();

}

if (isset($_SESSION['token'])) {
    $client->setAccessToken($_SESSION['token']);
    echo '<code>' . $_SESSION['token'] . '</code>';
}

// Check to ensure that the access token was successfully acquired.
if ($client->getAccessToken()) {
    try {
        // Call the channels.list method to retrieve information about the
        // currently authenticated user's channel.
        $channelsResponse = $youtube->channels->listChannels('contentDetails', array(
            'mine' => 'true',
        ));

        $htmlBody = '';
        foreach ($channelsResponse['items'] as $channel) {
            // Extract the unique playlist ID that identifies the list of videos
            // uploaded to the channel, and then call the playlistItems.list method
            // to retrieve that list.
            $uploadsListId = $channel['contentDetails']['relatedPlaylists']['uploads'];

            $playlistItemsResponse = $youtube->playlistItems->listPlaylistItems('snippet', array(
                'playlistId' => $uploadsListId,
                'maxResults' => 50
            ));

            $htmlBody .= "<h3>Videos in list $uploadsListId</h3><ul>";
            foreach ($playlistItemsResponse['items'] as $playlistItem) {
                $htmlBody .= sprintf('<li>%s (%s)</li>', $playlistItem['snippet']['title'],
                    $playlistItem['snippet']['resourceId']['videoId']);
            }
            $htmlBody .= '</ul>';
        }
    } catch (Google_ServiceException $e) {
        $htmlBody .= sprintf('<p>A service error occurred: <code>%s</code></p>',
            htmlspecialchars($e->getMessage()));
    } catch (Google_Exception $e) {
        $htmlBody .= sprintf('<p>An client error occurred: <code>%s</code></p>',
            htmlspecialchars($e->getMessage()));
    }

    $_SESSION['token'] = $client->getAccessToken();
} else {
    $state = mt_rand();
    $client->setState($state);
    $_SESSION['state'] = $state;

    $authUrl = $client->createAuthUrl();
    $htmlBody = <<<END
  <h3>Authorization Required</h3>
  <p>You need to <a href="$authUrl">authorise access</a> before proceeding.<p>
END;
}
?>

<!doctype html>
<html>
<head>
    <title>My Uploads</title>
</head>
<body>
<?php echo $htmlBody?>
</body>
</html>

Once you’ve got the script above and the Google Client Library API in place, load the script in your browser. You should be prompted to Authorise access before you can continue. Once you click on this link you should be taken through the steps to authorise your currently logged in Google account to access your new application.

Once it’s all done you should be redirected back to the localhost page, except now you will see a JSON string along the top of the page. Save this string somewhere for use later on. You might also notice if you’ve already uploaded videos to YouTube, a list of your videos on the page – just to confirm the access token works!

Saving your credentials

Now that you’ve received your refresh tokens create a text file called “the_key.txt” with the following information you’ve just received, it should look something similar to the format below:

{"access_token":"XXXXXXXXX","token_type":"Bearer", "expires_in":3600, "refresh_token":"XXXXXXX", "created":000000}

I suggest you write your refresh token down somewhere for safe keeping.

 

Creating the upload script

The script below is a very basic implementation. It requires the following file structure:

  • Google/
  • upload.php (the script below)
  • the_key.txt (the file we created in the previous section)
  • tutorial.mp4 (the video to upload)

The script will automatically update your “the_key.txt” file if your access_token is out of date.

Set the include path to ensure the Google API works correctly (line 3).

Replace the following variables with your own information:

  • $application_name
  • $client_secrect
  • $client_id
  • $videoPath
  • $videoTitle
  • $videoDescription
  • $videoCategory
  • $videoTags

 

$key = file_get_contents('the_key.txt');

set_include_path($_SERVER['DOCUMENT_ROOT'] . '/path-to-your-director/');
require_once 'Google/Client.php';
require_once 'Google/Service/YouTube.php';

$application_name = 'XXXXXX'; 
$client_secret = 'XXXXXXX';
$client_id = 'XXXXXXX.apps.googleusercontent.com';
$scope = array('https://www.googleapis.com/auth/youtube.upload', 'https://www.googleapis.com/auth/youtube', 'https://www.googleapis.com/auth/youtubepartner');
		
$videoPath = "tutorial.mp4";
$videoTitle = "A tutorial video";
$videoDescription = "A video tutorial on how to upload to YouTube";
$videoCategory = "22";
$videoTags = array("youtube", "tutorial");


try{
	// Client init
	$client = new Google_Client();
	$client->setApplicationName($application_name);
	$client->setClientId($client_id);
	$client->setAccessType('offline');
	$client->setAccessToken($key);
	$client->setScopes($scope);
	$client->setClientSecret($client_secret);

	if ($client->getAccessToken()) {

		/**
		 * Check to see if our access token has expired. If so, get a new one and save it to file for future use.
		 */
		if($client->isAccessTokenExpired()) {
			$newToken = json_decode($client->getAccessToken());
			$client->refreshToken($newToken->refresh_token);
			file_put_contents('the_key.txt', $client->getAccessToken());
		}

		$youtube = new Google_Service_YouTube($client);



		// Create a snipet with title, description, tags and category id
		$snippet = new Google_Service_YouTube_VideoSnippet();
		$snippet->setTitle($videoTitle);
		$snippet->setDescription($videoDescription);
		$snippet->setCategoryId($videoCategory);
		$snippet->setTags($videoTags);

		// Create a video status with privacy status. Options are "public", "private" and "unlisted".
		$status = new Google_Service_YouTube_VideoStatus();
		$status->setPrivacyStatus('private');

		// 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;

		// Setting the defer flag to true tells the client to return a request which can be called
		// with ->execute(); instead of making the API call immediately.
		$client->setDefer(true);

		// Create a request for the API's videos.insert method to create and upload the video.
		$insertRequest = $youtube->videos->insert("status,snippet", $video);

		// Create a MediaFileUpload object for resumable uploads.
		$media = new Google_Http_MediaFileUpload(
			$client,
			$insertRequest,
			'video/*',
			null,
			true,
			$chunkSizeBytes
		);
		$media->setFileSize(filesize($videoPath));


		// Read the media file and upload it chunk by chunk.
		$status = false;
		$handle = fopen($videoPath, "rb");
		while (!$status && !feof($handle)) {
			$chunk = fread($handle, $chunkSizeBytes);
			$status = $media->nextChunk($chunk);
		}

		fclose($handle);

		/**
		 * Video has successfully been upload, now lets perform some cleanup functions for this video
		 */
		if ($status->status['uploadStatus'] == 'uploaded') {
			// Actions to perform for a successful upload
			// $uploaded_video_id = $status['id'];
		}

		// If you want to make other calls after the file upload, set setDefer back to false
		$client->setDefer(true);

	} else{
		// @TODO Log error
		echo 'Problems creating the client';
	}

} catch(Google_Service_Exception $e) {
	print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage();
	print "Stack trace is ".$e->getTraceAsString();
}catch (Exception $e) {
	print "Caught Google service Exception ".$e->getCode(). " message is ".$e->getMessage();
	print "Stack trace is ".$e->getTraceAsString();
}

Suggestions

As you can see, the script is very primitive, among other things, this script should not be located in a publicly accessible directory due to the obvious abuse that could occur. In my situation, I created a queue of files to upload and setup a cron to execute the script once an hour that would then check a directory for any files of a certain type (mp4) and then loop through and upload to YouTube. The method I described hasn’t been tested on files more than 10 x 400mb in size so I’m not sure what the impacts would be on your server for more files / larger sizes etc.

Hopefully that’s enough to get your started, if you have any questions feel free to ask :)

Subscribe

You'll only get 1 email per month containing new posts (I hate spam as much as you!). You can opt out at anytime.

Categories

Leave a Reply

Your email address will not be published. Required fields are marked *

Preview Comment

26 Comments

  1. Maneesh tiwari
    https://www.domsammut.com/?p=1142#comment-2009

    Maneesh tiwari

    H i Dom Sammut ,

    Greet post !

    i am maneesh i have create all things . and my video is uploaded in my channel . i have one question can we set up a plate from for my register user add video in my channel through browser .

  2. mustyzod
    https://www.domsammut.com/?p=1142#comment-2008

    mustyzod

    Thanks for the detailed tutorial,it worked perfect.
    My question now is,if I don’t want the website user to get involved in the authentication,i guess I will need to make use of a service account, and that is where I lost.
    What is the flow?
    How can I implement this same upload without requiring the users to authenticate?
    Please I will appreciate your response even if it’s require you to mail me privately.
    It’s kind of very urgent.

  3. Anil
    https://www.domsammut.com/?p=1142#comment-2006

    Anil

    Hi,

    In google api i not found this file : YouTube.php

    please let me know what should i do.

    thanks

  4. tuyenvv
    https://www.domsammut.com/?p=1142#comment-2005

    tuyenvv

    Hi Dom Sammut
    When i check your code, i have found some issues
    In file upload video to Youtube line 37

    file_put_contents('the_key.txt', $client->getAccessToken());

    $client->getAccessToken() is an array not a tring then i changed to

    file_put_contents('access_key.txt', json_encode($client->getAccessToken()));

    I think it may help other devs.

  5. tuyenvv
    https://www.domsammut.com/?p=1142#comment-2004

    tuyenvv

    Thanks for sharing.
    Good job
    :)

  6. Angel Alvarez
    https://www.domsammut.com/?p=1142#comment-1999

    Angel Alvarez

    Felicidades Dom por tan exitoso Post.
    Mi duda es si es posible cancelar la carga de un Video una vez que ésta ya ha iniciado? esto es porque tengo un botón de Cancelar en donde necesito abortar la carga del video. Muchas gracias por tu tiempo.

  7. Angel Alvarez
    https://www.domsammut.com/?p=1142#comment-1998

    Angel Alvarez

    Dom congrats for this great post. My question is:

    When a video is uploading through Google_Http_MediaFileUpload object, is there a way to cancel the upload once it has begun the upload?

    Thanks;

    • Dom Sammut
      https://www.domsammut.com/?p=1142#comment-2001

      Dom Sammut

      Hey Angel,

      Thanks for the feedback :)

      You could potentially try and add a hook in the following bit of code to abort the upload inside the while statement.


              // Read the media file and upload it chunk by chunk.
              $status = false;
              $handle = fopen($videoPath, "rb");
              while (!$status && !feof($handle)) {
                  $chunk = fread($handle, $chunkSizeBytes);
                  $status = $media->nextChunk($chunk);
              }

      Hopefully that points you in the right direction a little bit!

      Cheers
      Dom

  8. Den Zubr
    https://www.domsammut.com/?p=1142#comment-1991

    Den Zubr

    Hi, it’s very usefull post, thanks ))) You mentioned, that you execute this script via cron. Interesting, how you could done this. Could you post the cron path to Google Client Library. Thank you.

    • Dom Sammut
      https://www.domsammut.com/?p=1142#comment-1992

      Dom Sammut

      Hey Den,

      Thanks for the feedback on the post! In regards to setting up the cron, it depends on the server you’re running PHP on. Most should have crontab or something similar. You can read more about how to setup crontab here.

      Cheers
      Dom

      • Den Zubr
        https://www.domsammut.com/?p=1142#comment-1996

        Den Zubr

        Hi, Dom,
        thank you for answer, I understood, what was my mistake: I didn’t write the absolute path to file with refresh token.

  9. Tausif Chauhan
    https://www.domsammut.com/?p=1142#comment-1990

    Tausif Chauhan

    Hello
    I have followed all step and program work perfectly.but after an hour my token get expired and im getting the below error. its not generating the refresh token on my the_key.txt file.

    im getting this error

    Caught Google service Exception 401 message is Error refreshing the OAuth2 token, message: ‘{ “error” : “invalid_client”, “error_description” : “The OAuth client was not found.” }’Stack trace is #0 /home/osp135/www/test/API/src/Google/Auth/OAuth2.php(259): Google_Auth_OAuth2->refreshTokenRequest(Array) #1 /home/osp135/www/test/API/src/Google/Client.php(368): Google_Auth_OAuth2->refreshToken(‘1/Joi_hTh_ggr8x…’) #2 /home/osp135/www/test/upload.php(51): Google_Client->refreshToken(‘1/Joi_hTh_ggr8x…’) #3 {main}

    My code is as below:

    /*****************************************************************/

    if($client->isAccessTokenExpired()) {
    echo “expired”;
    $newToken = json_decode($client->getAccessToken());
    //echo “”;
    //print_r($newToken);
    //exit;
    $client->refreshToken($newToken->refresh_token);

    var_dump ($client->getAccessToken());

    file_put_contents(‘the_key.txt’, $client->getAccessToken());
    }

    /*****************************************************************/

    im getting array after printing $newToken.
    but if var_dump ($client->getAccessToken()); im getting above error.

    please help ASAP.

    • Dom Sammut
      https://www.domsammut.com/?p=1142#comment-1995

      Dom Sammut

      Hey Tausif,

      Can you confirm you’re setting the access token like so when you load the script before you attempt to check if the access token is expired:


      $key = file_get_contents('the_key.txt');
      $client->setAccessToken($key);

      Cheers
      Dom

      • Mike Wang
        https://www.domsammut.com/?p=1142#comment-2011

        Mike Wang

        Hey Dom and Tausif,
        I’m having similar issue where $client->getAccessToken() returns array instead of string. So json_decode does not take it. Any idea? Did Youtube changed return data type?
        Thanks.

  10. Eugene
    https://www.domsammut.com/?p=1142#comment-1989

    Eugene

    I have a question.

    Your method requires user consent (because of OAUTH)
    And you said you are running cron jobs to upload videos.

    I don’t understand the setup.
    Once you consent, and you are logged in, and access token is valid, your cron doesn’t need human intervention to do its job?

    Thank you very much for the info btw.

    • Dom Sammut
      https://www.domsammut.com/?p=1142#comment-1993

      Dom Sammut

      Hey Eugene,

      Correct, once you consent and save the refresh token you will no-longer need human intervention. The method described in this post is designed so that you don’t have to ask for any user consent. This will allow you to upload videos to your own YouTube channel. The cron job can be used to regularly run the PHP script and check the directory for any new videos and upload them to YouTube. You could alternatively have the script fire after a user trigger action (although it’s not recommended as it will most likely time out the users session).

      If you are looking to upload videos to other users YouTube accounts, then yes, you’ll need to run the OAuth scenario each time.

      Cheers
      Dom

  11. Umesh
    https://www.domsammut.com/?p=1142#comment-1988

    Umesh

    i have set
    Uncaught exception ‘Google_Exception’ with message ‘Failed to start the resumable upload’

    • Dom Sammut
      https://www.domsammut.com/?p=1142#comment-1994

      Dom Sammut

      Hi Umesh,

      I’ll need a little bit more information than that!

      Cheers
      Dom

  12. Kishan
    https://www.domsammut.com/?p=1142#comment-1987

    Kishan

    I followed your tutorial to upload video but getting following exception.

    Caught Google service Exception 401 message is Error refreshing the OAuth2 token, message: ‘{ “error” : “unauthorized_client” }’

    Can you please help me ?

    Thanks.

  13. Armando Altieri
    https://www.domsammut.com/?p=1142#comment-1984

    Armando Altieri

    Hi Dom, thank you for the great works! You explained all things in clear way!

    I have a problem. I’v got the refresh_token, and for 1 hour all work fine.
    When I need to refresh the token, I receive an error “exception 401 message is Error refreshing the OAuth2 token”.
    I think this error is from Google, but I don’t understand the sense of this. The file “the_key-txt” is correct, nothing is changed.
    I tried to search on the web, but I didn’t find nothing useful. May you help me?

  14. Luis Pablo Gasparotto
    https://www.domsammut.com/?p=1142#comment-1971

    Luis Pablo Gasparotto

    Hi Dom,
    Thank you very, very, very much for this excellent post. Everything I needed was in the post or in the comments/replies. I don’t know if you’re able to edit the code in the post but if you can do that it would be great to add this between lines 97 and 98:
    //i.e. $uploaded_video_id=$status[‘id’];
    That’s in the posts but I think it’s the most important info to get after a successful upload.

    Also I’d add this line between 3 and 4:
    require_once ‘Google/autoload.php’;

    Finally, I’d add this link for downloading the right library:
    https://github.com/google/google-api-php-client/tree/v1-master
    Because the library in https://github.com/google/google-api-php-client is incomplete for what we need.

    Thank you very much again.

    • l
      https://www.domsammut.com/?p=1142#comment-1980

      l

      Hey Luis I am still ahving some issues with the refresh token. When it expires it doesnt get refreshed. IT says invalid grant.

  15. Soumya
    https://www.domsammut.com/?p=1142#comment-1970

    Soumya

    Hello Dom,
    Caught Google service Exception 400 message is Error refreshing the OAuth2 token, message: ‘{ “error” : “invalid_grant” }’Stack trace is #0 E:\xampp\htdocs\youtubeVideo\Google\Auth\OAuth2.php(259): Google_Auth_OAuth2->refreshTokenRequest(Array) #1 E:\xampp\htdocs\youtubeVideo\Google\Client.php(368): Google_Auth_OAuth2->refreshToken(‘AIzaSyDaMa13lQb…’) #2 E:\xampp\htdocs\youtubeVideo\index.php(52): Google_Client->refreshToken(‘AIzaSyDaMa13lQb…’) #3 {main}

css.php