Skip to content

YouTube API for CodeIgniter

World Wide Web Server edited this page Jul 4, 2012 · 17 revisions

Category:Libraries::YouTube API Category:Library::YouTube API

I have finished up work on the YouTube API library. This will let you make YouTube api calls without having to include all of the Zend Gdata libraries. This library has a single dependency which is on an OAuth signing helper I wrote which can be found at

[url]http://codeigniter.com/wiki/OAuth_Helper/[/url]

This library only works with OAuth authentication but a number of calls can be made without any authentication at all. You must pass in a few parameters while loading the library these are as follows:

Your YouTube API key this is given to you when you sign up as a developer with YouTube, and it is required. [code] $params['apikey'] = $this->config->item('youtube_api_key') [/code]

Also your oauth data if you are using authentication. You must provide your consumer key and secret, the signing algorithm you are using and if you already have an authenticated user you must pass in their OAuth access token data as an array containing the oauth_token and (if you are using HMAC signing) the oauth_token_secret. All values of the access_token array must be urlencoded.

I keep my static OAuth data in a config file
[code] $params['oauth']['key'] = $this->config->item('google_consumer_key'); $params['oauth']['secret'] = $this->config->item('google_consumer_secret'); $params['oauth']['algorithm'] = $this->config->item('google_signing_algo'); $params['oauth']['access_token'] = array('oauth_token'=>urlencode($token)); [/code]

After that we can load our library [code] $this->load->library('youtube', $params); [/code]

Now we have free reign to call any API method we choose. All methods will return a string of XML. It is your job to parse the XML and get out whatever you need. Some methods don't require an authenticated user but other do. Consult the library comments for more details. Here is a breakdown of all the methods that are available:

[code] getVideoEntry($videoId, $fullEntry = false) getRelatedVideoFeed($videoId) getVideoResponseFeed($videoId) getVideoCommentFeed($videoId) getTopRatedVideoFeed() getMostViewedVideoFeed() getRecentlyFeaturedVideoFeed() getWatchOnMobileVideoFeed() getPlaylistListFeed($user = 'default') getSubscriptionFeed($user = 'default') getContactFeed($user = 'default') getUserUploads($user = 'default') getUserFavorites($user = 'default') getUserProfile($user = 'default') getActivityForUser($user = 'default') getFriendActivityForCurrentUser() getInboxFeedForCurrentUser() getFormUploadToken($metadata) addComment($videoId, $comment, $commentId = false) [/code]

Methods where you define the user (by the users name) only require authentication if the user is defined as 'default' which indicates the currently logged in user. If a method is named get***ForCurrentUser then it requires authentication. Finally the getFormUploadToken method also requires an authenticated user to work.

In addition to requiring an authenticated user getFormUploadToken also requires some XML meta data be passed in. You can find out more about what this should look like at:

[url]http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html#Sending_a_Browser_Upload_API_Request[/url]

I wrote a library to get an OAuth access token from a google service it can be found at:

[url]http://codeigniter.com/wiki/OAuth_for_Google/[/url]

Here is the complete library. If you would rather download a copy you can do so at my blog:

[url]http://jimdoescode.blogspot.com/2010/10/codeigniter-and-youtube-api-this-is.html[/url]

[code] <?php class youtube { const HTTP_1 = '1.1'; const HOST = 'gdata.youtube.com'; const PORT = '80'; const SCHEME = 'http'; const METHOD = 'GET'; const LINE_END = "\r\n";

const URI_BASE = 'http://gdata.youtube.com/';

const DEBUG = true;

private $_uris = array(
    'STANDARD_TOP_RATED_URI'            => 'feeds/api/standardfeeds/top_rated',
    'STANDARD_MOST_VIEWED_URI'          => 'feeds/api/standardfeeds/most_viewed',
    'STANDARD_RECENTLY_FEATURED_URI'    => 'feeds/api/standardfeeds/recently_featured',
    'STANDARD_WATCH_ON_MOBILE_URI'      => 'feeds/api/standardfeeds/watch_on_mobile',
    'USER_URI'                          => 'feeds/api/users',
    'INBOX_FEED_URI'                    => 'feeds/api/users/default/inbox',
    'FRIEND_ACTIVITY_FEED_URI'          => 'feeds/api/users/default/friendsactivity',
    'ACTIVITY_FEED_URI'                 => 'feeds/api/events',
    'VIDEO_URI'                         => 'feeds/api/videos',
    'USER_UPLOADS_REL'                  => 'schemas/2007#user.uploads',
    'USER_PLAYLISTS_REL'                => 'schemas/2007#user.playlists',
    'USER_SUBSCRIPTIONS_REL'            => 'schemas/2007#user.subscriptions',
    'USER_CONTACTS_REL'                 => 'schemas/2007#user.contacts',
    'USER_FAVORITES_REL'                => 'schemas/2007#user.favorites',
    'VIDEO_RESPONSES_REL'               => 'schemas/2007#video.responses',
    'VIDEO_RATINGS_REL'                 => 'schemas/2007#video.ratings',
    'VIDEO_COMPLAINTS_REL'              => 'schemas/2007#video.complaints',
    'PLAYLIST_REL'                      => 'schemas/2007#playlist',
    'IN_REPLY_TO_SCHEME'                => 'schemas/2007#in-reply-to',
    'UPLOAD_TOKEN_REQUEST'              => 'action/GetUploadToken'
);

private $_header = array(
    'Host'=>self::HOST,
    'Connection'=>'close',
    'User-Agent'=>'CodeIgniter',
    'Accept-encoding'=>'identity'
);

private $_oauth = array();
private $_access = false;

/**
 * Create YouTube object
 *
 * @param string $clientId The clientId issued by the YouTube dashboard
 * @param string $developerKey The developerKey issued by the YouTube dashboard
 */
public function youtube($params)
{
    if(isset($params['apikey']))$this->_header['X-GData-Key'] = 'key='.$params['apikey'];
    $this->CI = get_instance();
    if(isset($params['oauth']))
    {
        $this->_oauth['key'] = $params['oauth']['key'];
        $this->_oauth['secret'] = $params['oauth']['secret'];
        $this->_oauth['algorithm'] = $params['oauth']['algorithm'];
        $this->_access = $params['oauth']['access_token'];
    }
}

private function _build_header($url = false, $prepend = false, $append = false, $method = self::METHOD)
{
    $str = $prepend === false ? '' : $prepend;
    foreach($this->_header AS $key=>$value)
        $str .= $key.": ".$value.self::LINE_END;
    if($this->_access !== false && $url !== false)
    {
        $this->CI->load->helper('oauth_helper');
        //TODO: Instead of static oauth values make these passed in by the access array.
        $str .= get_auth_header($url, $this->_oauth['key'], $this->_oauth['secret'], $this->_access, $method, $this->_oauth['algorithm']);
    }
    $str .= $append === false ? '' : $append;

    return $str;
}

private function _connect($host = self::HOST, $port = self::PORT, $ssl = false)
{
    $connect = $ssl === false ? 'tcp' : 'ssl';
    $opts = array(self::SCHEME=>array('method'=>self::METHOD, 'header'=>$this->_build_header()));
    $context = stream_context_create($opts);
    $handle = stream_socket_client($connect.'://'.$host.':'.$port, $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
    
    return $handle;
}

private function _check_status($handle)
{
    $gotStatus = false;
    $response = '';
    $resparray = array();
    while(($line = fgets($handle)) !== false && !$this->_timedout($handle))
    {
        $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false);
        if($gotStatus)
        {
            $response .= $line;
            array_push($resparray, $line);
            if(rtrim($line) === '')break;
        }
    }
    
    //Check the status if it's not 200 OK log the error and return false.
    $matches = explode(' ', $resparray[0]);
    
    if(!$gotStatus || $matches[1] != '200')
    {
        error_log('YouTube library received bad response: '.$response);
        if(!self::DEBUG)return false;
        else return $response;
    }
    return true;
}

private function _read($handle)
{
    if($this->_check_status($handle) !== true)return false;
    $response = '';
    //Get the chunk size
    $chunksize = rtrim(fgets($handle));
    //Convert hex chunk size to int
    if(ctype_xdigit($chunksize))$chunksize = hexdec($chunksize);
    else $chunksize = 0;
    
    if(self::DEBUG)error_log("\nCHUNKSIZE: {$chunksize}");
    
    while($chunksize > 0 && !$this->_timedout($handle))
    {
        $line = fgets($handle, $chunksize);
        //If fgets stops on a newline before reaching
        //chunksize. Loop till we get to the chunksize.
        while(strlen($line) < $chunksize)
            $line .= fgets($handle);

        $response .= rtrim($line);
        if(self::DEBUG)error_log("\nCHUNK: {$line}");
        
        $chunksize = rtrim(fgets($handle));
        //If we have a valid number for chunksize and we
        //didn't get an error while reading the last line
        if(ctype_xdigit($chunksize) && $line !== false)$chunksize = hexdec($chunksize);
        else break;
        
        if(self::DEBUG)error_log("\nCHUNKSIZE: {$chunksize}");
    }
    if(self::DEBUG)error_log("\nRESPONSE: {$response}");
    return $response;
}

private function _write($handle, $request)
{
    if(self::DEBUG)error_log($request);
    fwrite($handle, $request);
    return $request;
}

private function _timedout($handle)
{
    if($handle)
    {
        $info = stream_get_meta_data($handle);
        return $info['timed_out'];
    }
    return false;
}

private function _execute_request($uri)
{
    $request = self::METHOD." {$uri} HTTP/".self::HTTP_1.self::LINE_END;

    $url = self::URI_BASE.substr($uri, 1);

    $fullrequest = $this->_build_header($url, $request, self::LINE_END);

    $handle = $this->_connect();
    $this->_write($handle, $fullrequest);
    $output = $this->_read($handle);

    fclose($handle);
    $handle = null;

    return $output;
}

/**
 * Retrieves a specific video entry.
 *
 * @param $videoId The ID of the video to retrieve.
 * @param $fullEntry (optional) Retrieve the full metadata for the entry.
 *         Only possible if entry belongs to currently authenticated user.
 * @return the xml response from youtube
 */
public function getVideoEntry($videoId, $fullEntry = false)
{
    if($fullEntry)return $this->_execute_request ("/{$this->_uris['USER_URI']}/default/uploads/{$videoId}");
    else return $this->_execute_request ("/{$this->_uris['VIDEO_URI']}/{$videoId}");
}

/**
 * Retrieves a feed of videos related to the specified video ID.
 *
 * @param string $videoId The videoId of interest
 * @return the xml response from youtube.
 */
public function getRelatedVideoFeed($videoId)
{
    return $this->_execute_request("/{$this->_uris['VIDEO_URI']}/{$videoId}/related");
}

/**
 * Retrieves a feed of video responses related to the specified video ID.
 *
 * @param string $videoId The videoId of interest
 * @return the xml response from youtube.
 */
public function getVideoResponseFeed($videoId)
{
    return $this->_execute_request("/{$this->_uris['VIDEO_URI']}/{$videoId}/responses");
}

/**
 * Retrieves a feed of video comments related to the specified video ID.
 *
 * @param string $videoId The videoId of interest
 * @return the xml response from youtube.
 */
public function getVideoCommentFeed($videoId)
{
    return $this->_execute_request("/{$this->_uris['VIDEO_URI']}/{$videoId}/comments");
}

public function getTopRatedVideoFeed()
{
    return $this->_execute_request("/{$this->_uris['STANDARD_TOP_RATED_URI']}");
}

public function getMostViewedVideoFeed()
{
    return $this->_execute_request("/{$this->_uris['STANDARD_MOST_VIEWED_URI']}");
}

public function getRecentlyFeaturedVideoFeed()
{
    return $this->_execute_request("/{$this->_uris['STANDARD_RECENTLY_FEATURED_URI']}");
}

public function getWatchOnMobileVideoFeed()
{
    return $this->_execute_request("/{$this->_uris['STANDARD_WATCH_ON_MOBILE_URI']}");
}

public function getPlaylistListFeed($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}/playlists");
}

public function getSubscriptionFeed($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}/subscription");
}

public function getContactFeed($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}/contacts");
}

public function getUserUploads($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}/uploads");
}

public function getUserFavorites($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}/favorites");
}

public function getUserProfile&#40;$user = 'default'&#41;
{
    return $this->_execute_request("/{$this->_uris['USER_URI']}/{$user}");
}

public function getActivityForUser($user = 'default')
{
    return $this->_execute_request("/{$this->_uris['ACTIVITY_FEED_URI']}?author={$user}");
}

public function getFriendActivityForCurrentUser()
{
    if($this->_access !== false)return $this->_execute_request("/{$this->_uris['FRIEND_ACTIVITY_FEED_URI']}");
    else return false;
}

public function getInboxFeedForCurrentUser()
{
    if($this->_access !== false)return $this->_execute_request ("/{$this->_uris['INBOX_FEED_URI']}");
    else return false;
}

public function getFormUploadToken($metadata)
{
    if($this->_access !== false)
    {
        $uri = "/{$this->_uris['UPLOAD_TOKEN_REQUEST']}";
        $header = "POST {$uri} HTTP/".self::HTTP_1.self::LINE_END;
        
        $url = self::URI_BASE.substr($uri, 1);
        $encoding = "UTF-8";
        $extra = "Content-Type: application/atom+xml; charset={$encoding}".self::LINE_END;
        $extra .= "GData-Version: 2.0".self::LINE_END;
        mb_internal_encoding($encoding);
        $extra .= "Content-Length: ".mb_strlen($metadata.self::LINE_END).self::LINE_END.self::LINE_END;//If we hit deadlock change to length - 1
        
        $fullrequest = $this->_build_header($url, $header, $extra, 'POST');
        $fullrequest .= $metadata.self::LINE_END;
        
        $handle = $this->_connect();
        $this->_write($handle, $fullrequest);
        $output = $this->_read($handle);
        
        fclose($handle);
        $handle = null;
        
        return $output;
    }
    else return false;
}

/**
 * Add a comment to a video or a reply to another comment.
 * To reply to a comment you must specify the commentId
 * otherwise it is just a regular comment.
 *
 * @param string $videoId the video the comment goes with.
 * @param string $comment the comment
 * @param string (optional) $commentId the id of the comment to reply to.
 **/
public function addComment($videoId, $comment, $commentId = false)
{
    if($this->_access !== false)
    {
        $uri = "/{$this->_uris['VIDEO_URI']}/{$videoId}/comments";
        $header = "POST {$uri} HTTP/".self::HTTP_1.self::LINE_END;
        
        $url = self::URI_BASE.substr($uri, 1);
        $encoding = "UTF-8";
        $extra = "Content-Type: application/atom+xml; charset={$encoding}".self::LINE_END;
        $extra .= "GData-Version: 2.0".self::LINE_END;
        mb_internal_encoding($encoding);
        
        $xml = "&lt;?xml version='1.0' encoding='UTF-8'?&gt;<entry >";
        if($commentId !== false)$xml .= "&lt;link rel='http://gdata.youtube.com/schemas/2007#in-reply-to' type='application/atom+xml' href='{$url}/{$commentId}'/&gt;";
        $xml .= "<content>{$comment}</content></entry>";
        
        $extra .= "Content-Length: ".mb_strlen($xml.self::LINE_END).self::LINE_END.self::LINE_END;//If we hit deadlock change to length - 1
        $fullrequest = $this->_build_header($url, $header, $extra, 'POST');
        $fullrequest .= $xml.self::LINE_END;
        
        $handle = $this->_connect();
        $this->_write($handle, $fullrequest);
        $output = $this->_read($handle);
        
        fclose($handle);
        $handle = null;
        
        return $output;
    }
    else return false;
}

} // ./system/application/libraries ?> [/code]

EDIT: Updated the library to fix a bug with multi-chunked responses from youtube. Also added an API method to add a comment to a video.

EDIT: Fixed issue where newlines in youtube descriptions would cause the library to stop reading even if more data was available.

Clone this wiki locally