2
votes

So i managed to built oauth 2.0 youtube video upload, but everytime i upload a video i get an HTTP 400 ERROR with a invalid request.

But the weirdest thing is that the video is uploading to youtube while having : Failed (upload aborted).

im not using any framework, cause google doesnt have yet any to oauth 2.0, so i built all of my code on my own.

And also i did managed to send comments, and stuff.... the only problem is the video upload itself.

My code:

public function uploadVideo($video, $title, $description, $category, $keywords) {
$url       = 'http://uploads.gdata.youtube.com/feeds/api/users/FacebookDevelopersIL/uploads';
$boundary  = uniqid();

$accessToken = $this->refreshAccessToken("13", "11313", 'REFRESHTOKEN');
$xmlString = "<?xml version='1.0'?><entry xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/'  xmlns:yt='http://gdata.youtube.com/schemas/2007'><media:group><media:title type='plain'>".$title."</media:title><media:description type='plain'>".$description."</media:description> <media:category scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>".$category."</media:category><media:keywords>".$keywords."</media:keywords></media:group></entry>";
$videoData = file_get_contents($video);

$headers   = array(
    'POST /feeds/api/users/FacebookDevelopersIL/uploads HTTP/1.1',
    'Host: uploads.gdata.youtube.com',
    'Authorization: Bearer '.$accessToken,
    'GData-Version: 2',
    'X-GData-Key: key='.YOUTUBE_SRM_DEVELOPER_KEY,
    'Slug: IMG_0047.mp4',
    'Content-Type: multipart/related; boundary='.$boundary,
    'Content-Length:'.strlen($videoData),
    'Connection: close'
);

$postData  = "--".$boundary . "\r\n"
    ."Content-Type: application/atom+xml; charset=UTF-8\r\n\r\n"
    .$xmlString . "\r\n"
    ."--".$boundary . "\r\n"
    ."Content-Type: video/mp4\r\n"
    ."Content-Transfer-Encoding: binary\r\n\r\n"
    .$videoData . "\r\n"
    ."--".$boundary . "--";

$ch  = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
$response = curl_exec($ch);
curl_close($ch);
Trace::dump($response); }

The error im getting: HTTP/1.1 400 Bad Request Server: HTTP Upload Server Built on May 7 2012 18:16:42 (1336439802) Content-Type: text/html; charset=UTF-8 X-GUploader-UploadID: AEnB2Uq7cHcf6rS4bcamu18ChAF3gnKJqsF6U_dk2qB4WR9GhAoTL_-iUejitgead-Gh-1fpJcke1z68TAxoopS2vYiGmCW69A Date: Thu, 10 May 2012 11:55:24 GMT Pragma: no-cache Expires: Fri, 01 Jan 1990 00:00:00 GMT Cache-Control: no-cache, no-store, must-revalidate Content-Length: 15 Connection: close

Invalid Request

thanx everyone!

1

1 Answers

1
votes

Couple of things I notice: hard coding the POST and Host headers is poor form, because curl will take care of them automatically for you. I suspect part of the problem is the insertion of the carriage return/line feed between the $videoData and the last boundary marker. That will be interpreted as part of the video file. All you need are line feeds as line separators. Maybe the carriage return renders the video file invalid?

Maybe curl_setopt($ch, CURLOPT_VERBOSE, true) will provide some illumination.

This works for me (on a Linux host):

    /*
    **  https://developers.google.com/youtube/2.0/developers_guide_protocol_direct_uploading
    */
    private function send_to_youtube($video_file, $video_info) {

        //  Refresh access token
        log_msg("Obtaining access token");
        $response = http_post($this->config['token_url'], array(
            'client_id'     => $this->config['client_id'],
            'client_secret' => $this->config['client_secret'],
            'refresh_token' => $video_info->refresh_key,
            'grant_type'    => $this->config['grant_type']
        ));
        if ($response['http_code'] != 200) 
            throw new Exception("Unable to obtain access token. ".print_r($response, true));
        $authorization = json_decode($response['contents'], true);

        //  Build multi-part upload request
        //  api xml and then video file contents
        $boundary = uniqid();
        $location = '';        
        if ($video_info->latitude && $video_info->longitude) 
            $location = '
  <georss:where>
    <gml:Point>
      <gml:pos>'. $video_info->latitude .' '. $video_info->longitude .'</gml:pos>
    </gml:Point>
  </georss:where>';

        $content  = '--'.$boundary.'
Content-Type: application/atom+xml; charset=UTF-8

<?xml version="1.0"?>
<entry xmlns="http://www.w3.org/2005/Atom"
  xmlns:media="http://search.yahoo.com/mrss/"
  xmlns:yt="http://gdata.youtube.com/schemas/2007"
  xmlns:georss="http://www.georss.org/georss"
  xmlns:gml="http://www.opengis.net/gml">
  <media:group>
    <media:title type="plain">'. $video_info->title .'</media:title>
    <media:description type="plain">
      '. $video_info->description .'
    </media:description>
    <media:category
      scheme="http://gdata.youtube.com/schemas/2007/categories.cat">
      '. $video_info->category .'
    </media:category>
    <media:keywords>'. implode(', ', $video_info->tags) .'</media:keywords>
  </media:group>
'. $location .'
</entry>
--'.$boundary.'
Content-Type: '. $video_info->type .'
Content-Transfer-Encoding: binary

'.file_get_contents($video_file).'
--'.$boundary.'--';

        $headers = array(
            'Authorization: '.$authorization['token_type'].' '.$authorization['access_token'],
            'GData-Version: 2',
            'X-GData-Key: key='.$this->config['dev_key'],
            'Slug: '.$video_info->filename,
            'Content-Type: multipart/related; boundary="'.$boundary.'"',
            'Content-Length: '.strlen($content),
            'Connection: close'
        );

        //  Upload video
        log_msg("Sending video '{$video_info->title}', {$video_info->url}");
        $ch  = curl_init($this->config['upload_url']);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0);
        $response  = curl_exec($ch);
        $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if (!$http_code == 201) {           // Something other than 'New Entry'
            log_msg("Upload Failed: ".print_r($response, true));
            return new SimpleXMLElement();
        }

        $entry   = new SimpleXMLElement($response);
        $yt_link = $entry->link[0]->attributes()->href;
        log_msg("Upload Complete: ".$yt_link);

        return $entry;
    }