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 to Dom Sammut Cancel reply

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

Preview Comment

31 Comments

  1. Bikash Mondal
    https://www.domsammut.com/?p=1142#comment-1812

    Bikash Mondal

    Hi Dom Sammut,

    Thanks for your blog. Its great and simple to understand. I have used your process and it is uploading fine. But one problem i have found that if video is already uploaded then youtube is showing it is rejected because of duplicate. Can you please help me to find that this is a duplicate video or not?
    If it is a duplicate video the what is the main video? Please help me on this.

    Thanks,
    Bikash

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

      Dom Sammut

      Hi Bikash,

      This is all just speculative and I have not conducted any research into this.

      You can pull a list of videos from your account and attempt to pair up titles and/or timestamps and see if both values match, if so you could then put this video to the side for manual review.

      Alternatively, check to see if any particular codes get returned about the video you just uploaded. It might contain information that it has been flagged as a duplicate. If this was the case you could then do a second API call and delete the last uploaded video by the video ID provided in the response.

      Cheers
      Dom

      • Bikash Mondal
        https://www.domsammut.com/?p=1142#comment-1820

        Bikash Mondal

        Hi Dom,

        Thanks for reply,

        Yes i have checked the return values after upload. It is showing uploaded But after few min in video manager of youtube it is showing duplicate.

        Thanks,
        Bikash

  2. Chaminda
    https://www.domsammut.com/?p=1142#comment-1809

    Chaminda

    hi,
    Thanks for this tutorial. i need to upload a file from another server. for example i want to replace $videoPath = “tutorial.mp4”; with something like $videoPath = “http://myvpsserver/docs/media/tutorials/vid_1.mp4”; I tried it but i was not successfull? can we do this upload with a remote url ? if so pls help me.

    thanks in advance

    chaminda

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

      Dom Sammut

      Hi Chaminda,

      You’ll need to transfer the file to your server prior to initiating the script. If you have the URL this shouldn’t be too hard via wget/curl.

      Cheers
      Dom

  3. somnath sarkar
    https://www.domsammut.com/?p=1142#comment-1808

    somnath sarkar

    An error occurred: Error fetching OAuth2 access token, message: ‘invalid_grant’
    what should i do?

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

      Dom Sammut

      Hi Somnath,

      It sounds like you haven’t set up your project in Google properly. An invalid grant is usually triggered when you’re requesting something that your account is enabled for.

      Cheers
      Dom

  4. Artem
    https://www.domsammut.com/?p=1142#comment-1806

    Artem

    Hello, thank you for your posts.

    if I loop it – only the first video gets normally uploaded, other ones gets stuck at “Processing has started. ”

    getAccessToken()) {
    try{

    while(! feof($file_data))
    {
    $line_of_text = fgetcsv($file_data);
    $url = $line_of_text[0];
    $title = $line_of_text[1];

    $videoPath = “/var/www/html/$title.mp4”;

    $snippet = new Google_Service_YouTube_VideoSnippet();
    $snippet->setTitle($title);
    //$snippet->setDescription(“Test description”);
    //$snippet->setTags(array(“tag1”, “tag2”));

    $snippet->setCategoryId(“24”);

    $status = new Google_Service_YouTube_VideoStatus();
    $status->privacyStatus = “private”;

    // Associate the snippet and status objects with a new video resource.
    $video = new Google_Service_YouTube_Video();
    $video->setSnippet($snippet);
    $video->setStatus($status);

    // Specify the size of each chunk of data, in bytes. Set a higher value for
    // reliable connection as fewer chunks lead to faster uploads. Set a lower
    // value for better recovery on less reliable connections.
    $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);

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

    $htmlBody .= “Video Uploaded”;
    $htmlBody .= sprintf(‘%s (%s)’,
    $status[‘snippet’][‘title’],
    $status[‘id’]);

    $htmlBody .= ”;

    $time = date(“h:i:s”);

    $pieces = explode(“;;”, $line_of_text[0] . “;;” . $line_of_text[1] . “;;” . $time);

    fputcsv($file_report, $pieces);
    }

    fclose($file_data);
    fclose($file_report);

  5. Tihomir Sokolov
    https://www.domsammut.com/?p=1142#comment-1803

    Tihomir Sokolov

    Hi Dom,

    Thank you for working example!
    My work not start at the first time .. but after reading some comments job is done :)

    Best wishes,
    Tihomir

  6. Shahid
    https://www.domsammut.com/?p=1142#comment-1802

    Shahid

    Hi Dom:

    I have received the following error:
    Caught Google service Exception 0 message is Failed to start the resumable upload (HTTP 401: youtube.header, Unauthorized)Stack trace is #0 C:\wamp\www\yt\Google\Http\MediaFileUpload.php(136): Google_Http_MediaFileUpload->getResumeUri() #1 C:\wamp\www\yt\upload.php(90): Google_Http_MediaFileUpload->nextChunk(‘????ftypmp42???…’) #2 {main}

    I have read the possible solutions that you and others have mentioned here, try to apply these but still facing this issue
    I am receiving the refresh token in the first attempt as:
    “refresh_token”:”1\/soKWQ-XXXXX”,”created”:1430121612}

    saving this in the_key file, and on running the upload.php the above error displaying, while i have created new app too on google console. Can you give any advice?

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

      Dom Sammut

      Hi Shahid,

      Based off the information you have provided, I don’t really have any other suggestions unfortunately besides those I’ve already posted in the comments.

      Let me know if you work out what the problem was.

      Cheers
      Dom

  7. vinay
    https://www.domsammut.com/?p=1142#comment-1799

    vinay

    hi dom,

    actually everything works fine but if i want to upload videos in my personal playlist. so would you please tell me how can i do this.

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

      Dom Sammut

      Hi Vinay,

      You’ll need to sign in as the Google account that is attached to the personal YouTube account and then create a new project.

      Cheers
      Dom

  8. Alaa Sadik
    https://www.domsammut.com/?p=1142#comment-1791

    Alaa Sadik

    First, some Web developers and “specialist teams” have started copying & selling this very important code to clients without any reference to Dom and his blog. Please note that this is a copyrighted code and you should not distribute it without refering to the author in the code you provide to your clients.

    Second, for those who get the following error “Fatal error: Class ‘Google_Service’ not found” in /Google/Service/YouTube.php on line 32 please ensure that you included autoload.php as follow:
    require_once ‘google-api-php-client/src/Google/autoload.php’;

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

      Dom Sammut

      Hi Alaa,

      Thanks for highlighting this. If you could let me know who these developers are, that would be great for curiosities sake.

      Thanks for providing a solution to “Fatal Error” issue as well. I hope the implementation on your site is going well.

      Cheers
      Dom

  9. Martin Ocenas
    https://www.domsammut.com/?p=1142#comment-1789

    Martin Ocenas

    Hey Dom, just wanted to express a big thanks for this post. Keep up the good work.

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

      Dom Sammut

      Thanks Martin :)

  10. How
    https://www.domsammut.com/?p=1142#comment-1788

    How

    You save me tons of headaches! Thank you very much!

  11. Mirko Alberio
    https://www.domsammut.com/?p=1142#comment-1785

    Mirko Alberio

    Hello there,
    I’m trying to use your suggestion but i get this error on my upload.php:
    Caught Google service Exception 0 message is Failed to start the resumable upload (HTTP 401: youtube.header, Unauthorized)Stack trace is #0 /var/www/google-api-php-client/src/Google/Http/MediaFileUpload.php(138): Google_Http_MediaFileUpload->getResumeUri() #1 /var/www/video_upload_v3.php(97): Google_Http_MediaFileUpload->nextChunk(‘RIFF,O??AVI LIS…’) #2 {main}

    I see other users had similar errors and tried your helpful suggestions, but no way I can solve this. I have 1.0.5 API (tried other versions though) and right permission on google developer console.
    I can give you the client & media dumps below. Thanks-a-lot!

    object(Google_Client)#1 (8) {
      ["auth":"Google_Client":private]=>
      object(Google_Auth_OAuth2)#3 (4) {
        ["assertionCredentials":"Google_Auth_OAuth2":private]=>
        NULL
        ["state":"Google_Auth_OAuth2":private]=>
        NULL
        ["token":"Google_Auth_OAuth2":private]=>
        array(5) {
          ["access_token"]=>
          string(83) "ya29.UQG7xj3WihHsiD9qE7hJx5sQcTy_E79cRTNO8cUYJeZwW-GIWVhRutaJ6_5VUPQEsgo-2DFwolykyw"
          ["token_type"]=>
          string(6) "Bearer"
          ["expires_in"]=>
          int(3600)
          ["refresh_token"]=>
          string(45) "XXXXX"
          ["created"]=>
          int(1428676667)
        }
        ["client":"Google_Auth_OAuth2":private]=>
        *RECURSION*
      }
      ["io":"Google_Client":private]=>
      NULL
      ["cache":"Google_Client":private]=>
      NULL
      ["config":"Google_Client":private]=>
      object(Google_Config)#2 (1) {
        ["configuration":protected]=>
        array(6) {
          ["application_name"]=>
          string(5) "mirko"
          ["auth_class"]=>
          string(18) "Google_Auth_OAuth2"
          ["io_class"]=>
          string(16) "Google_IO_Stream"
          ["cache_class"]=>
          string(17) "Google_Cache_File"
          ["base_path"]=>
          string(26) "https://www.googleapis.com"
          ["classes"]=>
          array(4) {
            ["Google_IO_Abstract"]=>
            array(1) {
              ["request_timeout_seconds"]=>
              int(100)
            }
            ["Google_Http_Request"]=>
            array(2) {
              ["disable_gzip"]=>
              bool(true)
              ["enable_gzip_for_uploads"]=>
              bool(false)
            }
            ["Google_Auth_OAuth2"]=>
            array(13) {
              ["client_id"]=>
              string(72) "XXXXX.apps.googleusercontent.com"
              ["client_secret"]=>
              string(24) "XXXX"
              ["redirect_uri"]=>
              string(0) ""
              ["developer_key"]=>
              string(0) ""
              ["hd"]=>
              string(0) ""
              ["prompt"]=>
              string(0) ""
              ["openid.realm"]=>
              string(0) ""
              ["include_granted_scopes"]=>
              string(0) ""
              ["login_hint"]=>
              string(0) ""
              ["request_visible_actions"]=>
              string(0) ""
              ["access_type"]=>
              string(7) "offline"
              ["approval_prompt"]=>
              string(4) "auto"
              ["federated_signon_certs_url"]=>
              string(42) "https://www.googleapis.com/oauth2/v1/certs"
            }
            ["Google_Cache_File"]=>
            array(1) {
              ["directory"]=>
              string(18) "/tmp/Google_Client"
            }
          }
        }
      }
      ["deferExecution":"Google_Client":private]=>
      bool(false)
      ["requestedScopes":protected]=>
      array(3) {
        [0]=>
        string(46) "https://www.googleapis.com/auth/youtube.upload"
        [1]=>
        string(39) "https://www.googleapis.com/auth/youtube"
        [2]=>
        string(46) "https://www.googleapis.com/auth/youtubepartner"
      }
      ["services":protected]=>
      array(0) {
      }
      ["authenticated":"Google_Client":private]=>
      bool(false)
    }
    object(Google_Http_MediaFileUpload)#41 (11) {
      ["mimeType":"Google_Http_MediaFileUpload":private]=>
      string(7) "video/*"
      ["data":"Google_Http_MediaFileUpload":private]=>
      NULL
      ["resumable":"Google_Http_MediaFileUpload":private]=>
      bool(true)
      ["chunkSize":"Google_Http_MediaFileUpload":private]=>
      int(1048576)
      ["size":"Google_Http_MediaFileUpload":private]=>
      int(675840)
      ["resumeUri":"Google_Http_MediaFileUpload":private]=>
      NULL
      ["progress":"Google_Http_MediaFileUpload":private]=>
      int(0)
      ["client":"Google_Http_MediaFileUpload":private]=>
      object(Google_Client)#1 (8) {
        ["auth":"Google_Client":private]=>
        object(Google_Auth_OAuth2)#3 (4) {
          ["assertionCredentials":"Google_Auth_OAuth2":private]=>
          NULL
          ["state":"Google_Auth_OAuth2":private]=>
          NULL
          ["token":"Google_Auth_OAuth2":private]=>
          array(5) {
            ["access_token"]=>
            string(83) "ya29.UQG7xj3WihHsiD9qE7hJx5sQcTy_E79cRTNO8cUYJeZwW-GIWVhRutaJ6_5VUPQEsgo-2DFwolykyw"
            ["token_type"]=>
            string(6) "Bearer"
            ["expires_in"]=>
            int(3600)
            ["refresh_token"]=>
            string(45) "XXXXX"
            ["created"]=>
            int(1428676667)
          }
          ["client":"Google_Auth_OAuth2":private]=>
          *RECURSION*
        }
        ["io":"Google_Client":private]=>
        NULL
        ["cache":"Google_Client":private]=>
        NULL
        ["config":"Google_Client":private]=>
        object(Google_Config)#2 (1) {
          ["configuration":protected]=>
          array(6) {
            ["application_name"]=>
            string(5) "mirko"
            ["auth_class"]=>
            string(18) "Google_Auth_OAuth2"
            ["io_class"]=>
            string(16) "Google_IO_Stream"
            ["cache_class"]=>
            string(17) "Google_Cache_File"
            ["base_path"]=>
            string(26) "https://www.googleapis.com"
            ["classes"]=>
            array(4) {
              ["Google_IO_Abstract"]=>
              array(1) {
                ["request_timeout_seconds"]=>
                int(100)
              }
              ["Google_Http_Request"]=>
              array(2) {
                ["disable_gzip"]=>
                bool(true)
                ["enable_gzip_for_uploads"]=>
                bool(false)
              }
              ["Google_Auth_OAuth2"]=>
              array(13) {
                ["client_id"]=>
                string(72) "XXXX.apps.googleusercontent.com"
                ["client_secret"]=>
                string(24) "XXXX"
                ["redirect_uri"]=>
                string(0) ""
                ["developer_key"]=>
                string(0) ""
                ["hd"]=>
                string(0) ""
                ["prompt"]=>
                string(0) ""
                ["openid.realm"]=>
                string(0) ""
                ["include_granted_scopes"]=>
                string(0) ""
                ["login_hint"]=>
                string(0) ""
                ["request_visible_actions"]=>
                string(0) ""
                ["access_type"]=>
                string(7) "offline"
                ["approval_prompt"]=>
                string(4) "auto"
                ["federated_signon_certs_url"]=>
                string(42) "https://www.googleapis.com/oauth2/v1/certs"
              }
              ["Google_Cache_File"]=>
              array(1) {
                ["directory"]=>
                string(18) "/tmp/Google_Client"
              }
            }
          }
        }
        ["deferExecution":"Google_Client":private]=>
        bool(true)
        ["requestedScopes":protected]=>
        array(3) {
          [0]=>
          string(46) "https://www.googleapis.com/auth/youtube.upload"
          [1]=>
          string(39) "https://www.googleapis.com/auth/youtube"
          [2]=>
          string(46) "https://www.googleapis.com/auth/youtubepartner"
        }
        ["services":protected]=>
        array(0) {
        }
        ["authenticated":"Google_Client":private]=>
        bool(false)
      }
      ["request":"Google_Http_MediaFileUpload":private]=>
      object(Google_Http_Request)#25 (14) {
        ["batchHeaders":"Google_Http_Request":private]=>
        array(3) {
          ["Content-Type"]=>
          string(16) "application/http"
          ["Content-Transfer-Encoding"]=>
          string(6) "binary"
          ["MIME-Version"]=>
          string(3) "1.0"
        }
        ["queryParams":protected]=>
        array(2) {
          ["part"]=>
          string(14) "status,snippet"
          ["uploadType"]=>
          string(9) "resumable"
        }
        ["requestMethod":protected]=>
        string(4) "POST"
        ["requestHeaders":protected]=>
        array(2) {
          ["content-type"]=>
          string(7) "video/*"
          ["authorization"]=>
          string(90) "Bearer ya29.UQG7xj3WihHsiD9qE7hJx5sQcTy_E79cRTNO8cUYJeZwW-GIWVhRutaJ6_5VUPQEsgo-2DFwolykyw"
        }
        ["baseComponent":protected]=>
        string(33) "https://www.googleapis.com/upload"
        ["path":protected]=>
        string(18) "/youtube/v3/videos"
        ["postBody":protected]=>
        string(186) "{"snippet":{"categoryId":"22","description":"A video tutorial on how to upload to YouTube","tags":["youtube","tutorial"],"title":"A tutorial video"},"status":{"privacyStatus":"private"}}"
        ["userAgent":protected]=>
        NULL
        ["canGzip":protected]=>
        NULL
        ["responseHttpCode":protected]=>
        NULL
        ["responseHeaders":protected]=>
        NULL
        ["responseBody":protected]=>
        NULL
        ["expectedClass":protected]=>
        string(28) "Google_Service_YouTube_Video"
        ["accessKey"]=>
        NULL
      }
      ["boundary":"Google_Http_MediaFileUpload":private]=>
      bool(false)
      ["httpResultCode":"Google_Http_MediaFileUpload":private]=>
      NULL
    }
    

    Caught Google service Exception 0 message is Failed to start the resumable upload (HTTP 401: youtube.header, Unauthorized)Stack trace is #0 /var/www/google-api-php-client/src/Google/Http/MediaFileUpload.php(138): Google_Http_MediaFileUpload->getResumeUri()
    #1 /var/www/video_upload_v3.php(97): Google_Http_MediaFileUpload->nextChunk(‘RIFF,O??AVI LIS…’)
    #2 {main}

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

      Dom Sammut

      Hey Mirko,

      Have you tried a different video? It would appear you have authenticated properly but it appears to be getting stuck in the actual upload process.

      Let me know how it goes.

      Cheers
      Dom

      • Mirko Alberio
        https://www.domsammut.com/?p=1142#comment-1794

        Mirko Alberio

        Thanks Dom for the quick reply,
        no way, even with another exanoke Mp4 I downloaded from the web and and also an Avi file, same error.

    • Rob
      https://www.domsammut.com/?p=1142#comment-1821

      Rob

      Hi there,
      Have been using a variant of the sample script from google, but your script looks good and something that guards the refresh would be great :). However, I have three different youtube channels based on languages, is there a way I can specify which channel any one video is uploaded to?

  12. steeff
    https://www.domsammut.com/?p=1142#comment-1784

    steeff

    If you do not get the refresh_token from the dummy application to generate the OAuth token, add

    // after $client->setAccessType('offline');
    $client->setApprovalPrompt('force');

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

      Dom Sammut

      Thanks for this suggestion steeff :)

  13. RAmesh
    https://www.domsammut.com/?p=1142#comment-1780

    RAmesh

    hi dom
    i want to upload end users video on my youtube channel with my authentication without asking the end user authentication
    Can you Suggest me is it possible if yes then how??

    Regards & thanks
    Ramesh

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

      Dom Sammut

      Hi Ramesh,

      Yes it is possible.

      This tutorial runs through the process of harvesting a refresh token and uploading a video to YouTube server-side, without the end user having to authenticate with YouTube (or your channel).

      Cheers
      Dom

      • Ramesh
        https://www.domsammut.com/?p=1142#comment-1782

        Ramesh

        Hi i am geting this error can you tell me how to resolve this.Actually i am not that much perfect in php plz help me to achive this.

        Fatal error: Class name must be a valid object or a string in C:\mywebsite\www\MyWeb\web\web1\src\Google\Google_Client.php on line 104
        Thanks & Regards
        Ramesh

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

          Dom Sammut

          Hi Ramesh,

          A reasonable skill level in PHP is required to complete this tutorial. Unfortunately I won’t be able to help you with the basics of PHP. I suggest you read through the comments on this post and research the basics of PHP.

          Cheers
          Dom

  14. Ramesh
    https://www.domsammut.com/?p=1142#comment-1778

    Ramesh

    Where i will get the Json File

    • Devil8910
      https://www.domsammut.com/?p=1142#comment-1951

      Devil8910

      I have the issuse,pls hlpe me to fix this :(
      “Operation timed out after 100000 milliseconds with 0 bytes received”

css.php