Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP API Youtube Uploads, exception: Failed to start the resumable upload, upload must be sent to the upload URL

I'm trying to build a server application for uploading videos to YouTube. In my server application, the user can upload a video directly to my YouTube channel to make it public.

enter image description here

  1. The client part of my application acquires the video and uploads it to my server.
  2. My server then uses YouTube API to upload the video to my YouTube channel.

To make this work, I created a dummy Web Application that can capture the refresh token generated and I have stored it in a key.txt file

{"access_token":"MYTOKEN","token_type":"Bearer","expires_in":3600,"created":1435654774}

The upload_video.php script will automatically update "key.txt" file if access_token is out of date. This is the code from upload_video.php:

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

$application_name = 'YouTube_Upload'; 
$client_secret    = 'MY_CLIENT_SECRET';
$client_id        = 'MY_CLIENT_ID';
$scope            = array('https://www.googleapis.com/auth/youtube.upload', 'https://www.googleapis.com/auth/youtube', 'https://www.googleapis.com/auth/youtubepartner');

$videoPath        = "Test.f4v";
$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 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('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('unlisted');

        // 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
         */
        if ($status->status['uploadStatus'] == 'uploaded') {
            // Actions to perform for a successful upload
            // ......
        }

        // If 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 "Google_Service_Exception ".$e->getCode(). " message is ".$e->getMessage();
    print "Stack trace is ".$e->getTraceAsString();
}catch (Exception $e) {
    print "Exception ".$e->getCode(). " message is ".$e->getMessage();
    print "Stack trace is ".$e->getTraceAsString();
}

When the script runs, it raise this exception:

Exception 0 message is Failed to start the resume-able upload (HTTP 400: global, Uploads must be sent to the upload URL. Re-send this request to https://www.googleapis.com/upload/youtube/v3/videos?part=status,snippet&uploadType=resumable)Stack trace is 
#0 D:\xampp\htdocs\youtube\src\Google\Http\MediaFileUpload.php(136): Google_Http_MediaFileUpload->getResumeUri()
#1 D:\xampp\htdocs\youtube\resumable_upload.php(100): Google_Http_MediaFileUpload->nextChunk('\x00\x00\x00\x1Cftypf4v \x00\x00\x00...')
#2 {main}

exception raise on getResumeUri() (line 281) in Google_Http_MediaFileUpload, i have var dump the response from google

Google_Http_Request Object
(
    [batchHeaders:Google_Http_Request:private] => Array
        (
            [Content-Type] => application/http
            [Content-Transfer-Encoding] => binary
            [MIME-Version] => 1.0
        )

    [queryParams:protected] => Array
        (
            [part] => status,snippet
            [uploadType] => resumable
        )

    [requestMethod:protected] => POST
    [requestHeaders:protected] => Array
        (
            [content-type] => application/json; charset=UTF-8
            [authorization] => Bearer XXXXXXXXXXXXXXXX
            [content-length] => 187
            [x-upload-content-type] => video/*
            [x-upload-content-length] => 10201286
            [expect] => 
        )

    [baseComponent:protected] => https://www.googleapis.com//upload
    [path:protected] => /youtube/v3/videos
    [postBody:protected] => {"snippet":{"categoryId":"22","description":"A video tutorial on how to upload to YouTube","tags":["youtube","tutorial"],"title":"A tutorial video"},"status":{"privacyStatus":"unlisted"}}
    [userAgent:protected] => 
    [canGzip:protected] => 
    [responseHttpCode:protected] => 400
    [responseHeaders:protected] => Array
        (
            [x-guploader-uploadid] => XXXXXXXXXXXXXXXXXXXXXXXXXX
            [location] => https://www.googleapis.com/upload/youtube/v3/videos?part=status,snippet&uploadType=resumable
            [vary] => Origin
X-Origin
            [content-type] => application/json; charset=UTF-8
            [content-length] => 468
            [date] => Fri, 10 Jul 2015 09:54:30 GMT
            [server] => UploadServer
            [alternate-protocol] => 443:quic,p=1
        )

    [responseBody:protected] => {
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "wrongUrlForUpload",
    "message": "Uploads must be sent to the upload URL. Re-send this request to https://www.googleapis.com/upload/youtube/v3/videos?part=status,snippet&uploadType=resumable"
   }
  ],
  "code": 400,
  "message": "Uploads must be sent to the upload URL. Re-send this request to https://www.googleapis.com/upload/youtube/v3/videos?part=status,snippet&uploadType=resumable"
 }
}

    [expectedClass:protected] => Google_Service_YouTube_Video
    [expectedRaw:protected] => 
    [accessKey] => 
)

What is wrong? Thanks for any help and sorry for bad English.

like image 597
ar099968 Avatar asked Jun 30 '15 10:06

ar099968


3 Answers

This seems to be a problem with PHP Client Library of Google API. Go to GOOGLE_LIB_PATH/Http/MediaFileUpload.php and replace this line:

$this->request->setBaseComponent($base . '/upload'); 

With this one:

$this->request->setBaseComponent($base . 'upload'); 

Try again and share the results. I faced a similar problem with Google Pubsub API where API path set by library was incorrect.

like image 156
Furhan S. Avatar answered Nov 08 '22 20:11

Furhan S.


I hope you need Resumable Uploads(https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol)

Then use this without any modification.

Also check if the video format is supported from https://support.google.com/youtube/troubleshooter/2888402?hl=en

like image 33
Mithun John Jacob Avatar answered Nov 08 '22 19:11

Mithun John Jacob


There is a google method to get the refresh token, so instead of using json_decode :

$newToken = json_decode($client->getAccessToken());
$client->refreshToken($newToken->refresh_token);

You can do :

$client->refreshToken( $client->getRefreshToken() );
like image 30
Agung Santoso Avatar answered Nov 08 '22 20:11

Agung Santoso