Tuesday, October 12, 2010

CodeIgniter and the YouTube API. This is a triumph


 Please note that the code for this has been moved to GitHub Here


I just want to make a note here, 'huge success'.

I finished work on my youtube API library for CodeIgniter. This will let you connect to youtube and make requests without having to load any Zend GData libraries. It is written exclusively for CodeIgniter.

To use the library you must first pass in some parameters. The apikey is the apikey provided to you once you register with youtube to use the API (I put mine in a config file). Next you want to include the oauth consumer key that you setup when you setup oauth with google. The secret is the consumer secret google provides you if you are using HMAC-SHA1 signing or the file path to your .pem file if you are using RSA-SHA1 signing (again I put these in a config file for easy access/changes). You must then specify the signing algorithm you use to make your oauth signatures. Finally you have to specify the access token and token secret if your user has already authorized your site to connect to youtube on their behalf.

If any of this seems over your head please consult the oauth docs on youtube for further clarification. You can find those here. It should also be noted that this library only does OAuth authentication it does not support AuthSub feel free to add this if you would like though.

This library does require the oauth_helper I wrote for CodeIgniter I will link to the file at the bottom of the page but if you wish to further understand what it does visit the CodeIgniter Wiki here. Also I wrote a library to handle all of the OAuth interactions that is also available on the Wiki here. (I will provide a link to this at the bottom of this post too.)

$params['apikey'] = $this->config->item('youtube_api_key');
$params['oauth']['key'] = $this->config->item('google_consumer_key');
$params['oauth']['secret'] = $this->config->item('google_consumer_secret');
$params['oauth']['algorithm'] = 'RSA-SHA1';
$params['oauth']['access_token'] = array('oauth_token'=>urlencode($token));
$this->load->library('youtube', $params);

That's it you now have a youtube API instance and can make calls. The following methods are available please consult the documentation for usage etc.

Here is a list of methods that are available right out of the box, no authentication needed.

$this->youtube->getVideoEntry($videoId);
$this->youtube->getRelatedVideoFeed($videoId, $params = array());
$this->youtube->getVideoResponseFeed($videoId, $params = array());
$this->youtube->getKeywordVideoFeed(array $keywords, $params = array());
$this->youtube->getVideoCommentFeed($videoId, $params = array());
$this->youtube->getTopRatedVideoFeed($params = array());
$this->youtube->getMostViewedVideoFeed($params = array());
$this->youtube->getRecentlyFeaturedVideoFeed($params = array());
$this->youtube->getWatchOnMobileVideoFeed($params = array());

The list of methods below requires an authenticated user before they can be used.

$this->youtube->getUserPlaylistFeed($user = 'default', $params = array());
$this->youtube->getPlaylistFeed($playlistid, $params = array());
$this->youtube->getSubscriptionFeed($user = 'default', $params = array());
$this->youtube->getContactFeed($user = 'default', $params = array());
$this->youtube->getUserUploads($user = 'default', $params = array());
$this->youtube->getUserFavorites($user = 'default', $params = array());
$this->youtube->getUserProfile($user = 'default');
$this->youtube->getUserActivity($user = 'default');
$this->youtube->getInboxFeedForCurrentUser();

Finally these methods all require some type of data to be specified whether that be XML meta data, a comment or a boolean value.

$this->youtube->directUpload($path, $contenttype, $metadata, $user = 'default');
$this->youtube->getFormUploadToken($metadata);
$this->youtube->addComment($videoId, $comment, $commentId = false);
$this->youtube->addVideoResponse($videoId, $responseId);
$this->youtube->addNumericRating($videoId, $rating);
$this->youtube->addLikeDislike($videoId, $like);

All methods where the user is defined as 'default', or are getting data from the current user, and the form upload token method, all require oauth authentication or they wont work. The methods that let you define the user may work without authentication but will only show data that the specified user has made public. The form upload token method requires xml meta data to be passed in as well. You can see what this should look like here. All API request return XML and it is your job to parse it and get what you want from it.  

I should note that I haven't tested all of the methods defined in this library so I cannot say with 100% certainty that they all work, but from the ones I have tested I can make a pretty good assumption that they do. Also I used RSA-SHA1 signing for my OAuth requests I just couldn't get HMAC to work properly I found an issue with my signing method for HMAC and fixed it but I cannot guarantee that it works properly either. If you have an issue post it in the comments and give me your HTTP header information for both your request and the response. There is a DEBUG constant flag you can set that will output this information to your error logs. I am also going to post a more technical brief of this on the CodeIgniter wiki which I will link to here.

[EDIT] There seems to be some confusion about how all this stuff fits together so let me clarify the steps. Please Please Please read all the documentation before leaving a comment.
  1. Download the linked files and put them in the correct CI directories. You need all 3 of them. Two go in the application/libraries folder and one goes in the application/helpers folder.
  2. Read the documentation on OAuth. Having a basic understanding of how it works is key. Make sure you read the oauth_helper and google_oauth wiki pages. Both contain vital information about generating the necessary keys and how to use those files. Read the oauth_helper wiki first. It has links on generating some keys and how to use them. Don't worry too much about understanding how oauth_helper works it is more for internal purposes but you do need to checkout the links in its wiki. Read the google_oauth wiki next as it provides more working examples of what you need to do to get oauth up and running and get the necessary access token.
  3. Make your controller to get a request and access token. Samples are provided on the google_oauth wiki page. If you have read the documentation up to this point then it shouldn't be too difficult to figure out what to do. Just use the google_oauth wiki controller examples for reference.
  4. Test your controller methods out and make sure you are getting an access token and there are no hiccups in the 3 step process. You can get an access token as many times as you need so don't worry if you messed something up and need to get a new one.
  5. Read the youtube api wiki page and check out the official youtube api documentation which can be found here
  6. Make your controller to access the youtube api. You will pass in the access token you got from the previous steps as well as an API key you get from google. Make sure you read up on all of the methods the api provides. It is by no means a complete API library there are a number of missing features which may get added in the future but my hope is that you can figure out how to add the things you need by reading the official youtube api documentation and looking at how I implemented things in the api library. Really what it boils down to is HTTP header manipulation.
  7. Finally put everything together. The process should be pretty seamless with the user only having to leave your site for a split second to approve the request token. Also note that you can do this via a local server so even if you setup everything to your sites domain if you are running a dev instance locally everything should still work.
If you have read everything on this blog post and all the subsequent pages it links to and you still have questions please let me know. Judging from some of the questions I have had all of the info is in the blog post and the linked pages I think the problem is: there isn't a clear order to it all. For that I apologize. Follow the steps outlined and it should make things a bit easier.

183 comments:

  1. awesome I am gonna try this out. thank you very much

    ReplyDelete
  2. hi Jim,

    great job. i have one question regarding methods.

    i get this message

    A PHP Error was encountered

    Severity: Notice
    Message: Undefined offset: 0
    Filename: libraries/youtube.php
    Line Number: 147

    ReplyDelete
  3. Hey sorry about the delay I haven't been keeping up with stuff. Anyway I updated the library so go get the latest version and see if that will fix the issue.

    ReplyDelete
  4. Actually yeah looking at the old version the new version should fix that issue. I removed the preg_match which is causing your problem and am getting the XML directly now.

    ReplyDelete
  5. Wat about Direct Uploading files of a user from our servers to youtube??

    ReplyDelete
  6. It currently doesn't support direct uploading as defined here: http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html#Direct_uploading

    Right now the only upload method is browser based uploading via the upload token as defined here: http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html#Browser_based_uploading

    I will add direct uploading just in case.

    ReplyDelete
  7. In response to Neil I updated the library to allow direct uploads. There are also some additional untested methods for things such as rating a video or liking a video and setting video responses. Give those a shot and let me know if you have any problems.

    ReplyDelete
  8. How do i get the $params['oauth']['access_token'] ?

    ReplyDelete
  9. You should use the oauth library and helper I wrote for CodeIgniter. Check the post above for links to those files and for links to their wiki articles.

    ReplyDelete
  10. Your code above does not mention any function from the Oauth library. To get the access token we have to use the $this->oauth library->get_access_token ???

    ReplyDelete
  11. This comment has been removed by the author.

    ReplyDelete
  12. I keep getting signature_invalid base_string as the response of the $this->_connect() function within get_request_token()

    ReplyDelete
  13. This is while i am using the HMAC_SHA1 method

    ReplyDelete
  14. You need to read the wiki articles that go along with this. Here is the one about the oauth library http://codeigniter.com/wiki/OAuth_for_Google/

    ReplyDelete
  15. What was the error with your signing method for HMAC_SHA1? I keep getting ignature_invalid base_string as the response instead of the request token and a token secret.

    ReplyDelete
  16. The HMAC signing method should be correct, but I did read several places that people using it were having similar problems and their algorithms seemed to be correct. I really recommend using RSA, it is much easier to deal with (you don't have to worry about the token secrets), and I know for a fact that it works.

    ReplyDelete
  17. This comment has been removed by the author.

    ReplyDelete
  18. Neil, did you have to make any changes to the oauth library to get it to work with HMAC? If so could you please outline them so that I can implement it.

    ReplyDelete
  19. I reverted back to RSA , HMAC is a real pain .When the video is flagged as private the getUserUploads does not return anything. Is there a fuction to view private uploads of an account?

    ReplyDelete
  20. If you are specifying a user that is not you then you wont be able to see their private uploads. You can only see your own private uploads.

    ReplyDelete
  21. This comment has been removed by the author.

    ReplyDelete
  22. I keep getting a 401 unauthorized authentication header as the response for any request

    ReplyDelete
  23. Also is there any method to delete videos

    ReplyDelete
  24. Without seeing your header I cannot diagnose what the issue is. I would suggest using the oauth playground that google provides to see what portions of the header you are missing.

    http://googlecodesamples.com/oauth_playground/

    There isn't a method in the library to delete at the moment, I suppose I can add one but it might be a bit since it is getting close to the holidays and I am a bit behind on some other things.

    ReplyDelete
  25. Jim is there any way to delete comments thru the API??

    ReplyDelete
  26. what about pagination ?? how do i page through the results

    ReplyDelete
  27. Neil, right now these things aren't supported I can add them, though, I don't know when I will have time at the moment.

    Feel free to add those features to the library if you need them in a timely manner.

    ReplyDelete
  28. hey Jim, nice stuff here, really excited to get it working. i'm a little hung up and hoping you have some insight.

    when getting the oAuth token, you use:
    get_auth_header('https://bla/OAuthGetRequestToken', 'mysite.com', './path/to/private/cert', ....

    you have a work-around for a host without a private certificate (no SSH, cant generate cert)

    i thought using the RSA-SHA1 algorithm did not need a certificate.
    anyway, thanks for taking the time to add to CI, glad to see people are still adding functionality

    ReplyDelete
  29. No you don't need to have SSL. The cert file I am talking about there is the RSA certificate file. You can generate one of these via the command line. Just google how to do it. Then you have to upload that file to google and retain a copy for yourself. The path you specify there is the path to that RSA cert file.

    Make sure that that certificate file is out of your webroot or deny access to it some other way, since that is how everything will be signed.

    ReplyDelete
  30. humm... have certificate now, but looks like there is an error, line 53 of oauth_helper, sign_rsa_sha1($method, $baseurl, $certfile...

    but when sign_rsa_sha1 is called via build_auth_array(), it passes $secret where $secrete is the consumer secret string, rather than a path to certificate?

    so i tried calling build_auth_array with certificate path set for $secret but hit an openssl_sign() error.

    by chance do you have a sample controller you could post, i'm guessing i have something out of order in my authentication route.

    ReplyDelete
  31. None of my controllers use the oauth_helper functions and the blog post above demonstrates how to call the youtube api. Which is what you would use in a controller.

    The oauth_helper is more for the apis that require oauth authentication it is pretty technical so I wouldn't recommend calling any of its functions if you aren't pretty familiar with oauth, and there really isn't a need to anyway since the youtube api takes care of all of that for you.

    You should be specifying all of your authentication data when you call the youtube api library as demonstrated above in the blog post.

    If you are getting signing errors check to make sure you are specifying the correct file as your RSA certificate file (I believe when you generate one it generates two different files and only one is the certificate).

    Are you writing your own api that requires oauth? I am a bit confused as to why you are attempting to use the oauth_helper instead of the youtube api.

    ReplyDelete
  32. Hey Jim, thanks for the clarification, i think i'm back on track, pretty new to oAuth, been going in circles trying to get things to work.

    i'm still a little hung up on getting the access_token though.

    per your CI wiki entry(http://codeigniter.com/wiki/YouTube_API_for_CodeIgniter/) i just need to pass in some params and then load the youtube library, but where/how do i get the access_token? eg:$params['oauth']['access_token'] = array('oauth_token'=>urlencode($token));

    was hoping to see a sample controller just to see how you are initiating your library

    thanks for the help

    ReplyDelete
  33. Please read this http://code.google.com/apis/youtube/2.0/developers_guide_protocol_oauth.html it will help you understand how an oauth transaction works.

    Once you read that make sure you have all 3 files downloaded (youtube.php, oauth_helper.php, and google_oauth.php) and put in the correct directories. The google_oauth.php library has two methods. One gets a request token and the other gets an access token.

    There are links in the blog post for documentation on all of the files I listed. Like I said earlier though you really shouldn't have to change any of them.

    I think once you know how an oauth token transaction works then you will have a better understanding of what you need to do.

    If you are still having problems let me know I can provide a sample controller which might clarify things.

    ReplyDelete
  34. yeah, if you have anything showing how you initiate all the libraries/helpers that would be good.

    my understanding of oAuth is something like this:
    initiate authentication, user gives access, and returns to callback
    at callback retrieve token, use for api calls (where auth is required)
    it's a pretty distilled, but i think it's on the right track.

    ReplyDelete
  35. scratch that just made a positive connection, but you might want to post an example for others too?

    ReplyDelete
  36. You are close.

    The transaction is as follows:

    Request a request token from youtube and provide URL to return to with access token.

    Redirect to youtube providing request token.

    User gives your site permission to access API.

    Youtube redirects back to your site at the specified return URL with access token.

    Store access token and use it for all subsequent API calls.

    Sample controller methods can be found at the wiki pages outlined in the blog post. Here is the link http://codeigniter.com/wiki/OAuth_for_Google/

    ReplyDelete
  37. I updated this blog post with a clearer set of steps to get the API working. Reading through the comments it is pretty apparent that the reason folks are having trouble is because everything is kind of scattered about. Hopefully going step by step down the list above will clarify things better.

    ReplyDelete
  38. Jim,

    I applaud your work on this and send many thanks your way.

    However, being the CI/oauth noob that I am, I'm still having a bit of difficulty pulling this all together.

    Maybe you would be so kind as to point me in the right direction. As of right now I have CI up and running with the above libraries in the proper location. It is my understanding that i now need to construct a controller using the example code from the Oauth for google page.

    Thanks a ton for your hard work in putting this all together.

    ReplyDelete
  39. You seem to be on the right track. Have you generated your own RSA key file and uploaded it to google? After you have done those things then the oauth controller methods outlined in the oauth for google wiki page should work.

    Can you be more specific as to what issues you are having?

    ReplyDelete
  40. hey Jim, thanks for all the help, think you're updated instructions helped a lot. I have a follow up question you might be able to help with. i've set my controller up and make an authentication request, where i grant access form YT, bounce back to my callback method and then am simply printing out a few requests. where i'm running into issues is getting the insight link node, for a video feed. the feed i'm returning does not have an insights node, and it looks like i need to authenticate that request to include the insights node. "A video entry contains a link to an Insight data feed if the authenticated user retrieving the entry owns the video." per: http://bit.ly/cLq6O0

    you also mention "Methods where you define the user (by the users name) only require authentication", so i tested the inbox feed request, getInboxFeedForCurrentUser() and am getting data, so at a bit of a loss for ideas here, any ideas?

    ReplyDelete
  41. I am not sure that you can get an insight node from a feed. I think you can only get it when you are retrieving a single entry or possibly when you are getting a list of your own uploads.

    To get it from a single entry you could call the getVideoEntry method and try setting the fullEntry parameter to true if you aren't getting what you need from the standard call.

    It seems to me though that it doesn't make sense to include insight data in a feed of videos since you would need to look at each video in the feed and determine who it belongs to (you might be able to get insight nodes if you are using the getUserUploads method, I never tested that though). But if you are getting a single entry it is much easier for youtube to check who the video belongs to and add in extra data if deemed appropriate.

    What I would recommend is two fold:

    First try the getUserUploads method and see if the video feed returned contains the insight nodes. (Note that you do not specify a user if you want to pull from the currently authenticated user)

    Second try the getVideoEntry method setting the fullEntry parameter to true and see if that will get you what you need.

    Post back and let me know if either of those work. If not please specify which method from the API you are using to retrieve the feed so that we can verify if it is something that needs to be added or not.

    ReplyDelete
  42. yup, shoulda mentioned that was the route i was already taking, getting uploads by user feed, extracting entries & entry id, and then querying for single entry feed, which should have the insights link node. here's the loop i was running:

    $output = $this->youtube->getUserUploads();
    $xml = simplexml_load_string($output);

    # $xml is now an object of user's upload feed.
    # will have at least 50 videos for user, loop through entries and
    # get CSV url from link node. (must be authenticated user to get CSV node)
    # not f'ing authenticaed yet??
    $i = 0;
    // single entry for testing
    foreach($xml->entry as $entry){
    if($i > 0) return false;
    foreach($entry->id as $key=>$val){
    $id = explode('videos/', (string)$val);
    $v = $this->youtube->getVideoEntry($id[1]);
    print_r($v);
    }
    $i++;
    }

    ReplyDelete
  43. Is this working? My only concern is that you could be temporarily locked out of the API for making too many subsequent calls too quickly.

    Also as another user pointed out you may only get a subset of the users actual uploads. The API only returns a set amount (I can't remember what the number is) any videos beyond that are not returned. I plan on updating the library to allow for paging the results but have yet to do so.

    ReplyDelete
  44. nope, not working, and yeah there is a limit, i believe it's the last 50 videos for user, for now i've just been getting last 50, and then extracting first two video entry id's, then calling the getVideoEntry method to return a feed for each, but neither have the insights node.

    i've been crawling through google's api and found something that mentioned the version when making an api call, it siggests appending v=2 to your request to use the 2.0 api, i just ran a modified request:

    "/{$this->_uris['USER_URI']}/default/uploads/{$videoId}?=2"

    and with the ? url encoded, but received no results at all.

    ReplyDelete
  45. You tried setting the fullEntry flag and that still didn't get you what you needed?

    $v = $this->youtube->getVideoEntry($id[1], true);

    ReplyDelete
  46. yeah, i've modified your method to always return a full entry. take a look at this, maybe something will jump out, http://pastebin.com/jR82U0xz

    ReplyDelete
  47. In that snippet you are calling getRelatedVideoFeed which I don't think will have insight nodes in its results.

    Try this:

    Get the id of a video you uploaded and call getVideoEntry on it directly and see if you get insight nodes on that.

    print_r($this->youtube->getVideoEntry('XXXXXX', true));

    ReplyDelete
  48. yup, i've been at this a while and was testing a few things out, that loop should be:

    foreach($entry->id as $key=>$val){
    $n = explode('videos/', (string)$val);
    $r = $this->youtube->getVideoEntry($n[1]);
    print_r($r);
    }

    which still does not return an insights node

    ReplyDelete
  49. Well I am all out of ideas. I'm going to have to do some more research. There very well could be some other call that yields those nodes. Sorry I couldn't be of more help.

    ReplyDelete
  50. Howdi Jim,

    I've been hacking at getting your Google_Oauth lib, Oauth_helper, and Youtube lib working with my open source application yet all to no avail. I upgraded the lib to be PHP 5 / CI 2.0 compliant.

    I've got all the libs, helper, and config file in the right places. Here is what I'm doing with my controller as per your code exp: on Google_Oauth CI wiki:

    https://github.com/socialigniter/youtube/blob/master/controllers/connections.php

    Problem 1:
    Severity: Notice
    Message: Undefined index: oauth_token
    Filename: libraries/Google_oauth.php
    Line Number: 95

    Problem 2:
    Severity: Notice
    Message: Undefined index: oauth_token_secret
    Filename: libraries/Google_oauth.php
    Line Number: 104

    I understand what those errors mean, and even hack work arounds (defining the at the array above the constructor), but that solution leads to:

    Problem 3:
    Upon being sent to Google I get an "Invalid Token" error when I land on this URL
    https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=

    I'm sure I'm missing something totally boneheaded, so hopefully you can help me in the right direction!

    Cheers, BN

    ReplyDelete
  51. Looking at your code you should not specify an access token in your request. The access token is the very last thing that you get and is what you use to authenticate with, so when you are making a request you don't have an access token.

    Also I would suggest using RSA-SHA1 signing. I don't know what the problem is, but I was never able to get HMAC signing to work despite having the correct signing algorithm. I know the algorithm is correct because it works with Twitter. I also read some places that people were having trouble with HMAC and authenticating with Google. These issues have led me to recommend using RSA-SHA1. Another benefit to RSA is you don't need to store token secrets to sign things with.

    My recommendations are to:

    Remove the access token array parameter from your request method and see if that fixes the issue. If it does not, try switching to RSA-SHA1 signing and see if that will fix the issue.

    ReplyDelete
  52. I got the HMAC-SHA part to work in your help by urlencoding the secret word and oauth_token_secret parameter:

    $secret = urlencode( $secret );
    $secret .= '&'.urlencode( $parameters['oauth_token_secret'] );

    ReplyDelete
  53. I will have to test that out. Might be better to urlencode the secret in the google_oauth library instead of the oauth_helper since I know that the oauth_helper works with sites like twitter without signing the secret.

    Thanks for the insight though :)

    ReplyDelete
  54. Neil I am not sure if it is still relevant to your situation but I have updated the youtube api library to add paging.

    ReplyDelete
  55. Actually wildrhombus, according to the docs, the secret should be signed before the google_oauth library is even loaded. You should urlencode your secret in your controller.

    ReplyDelete
  56. Hello and thanks for the API.

    Been trying to get it work but when using a certificate on RSA I get the following error: Message: fopen(N7g9hS1wOJ9KlYH_qfR71Tgy) [function.fopen]: failed to open stream: No such file or directory.

    It seems like the code is looking for the .pem file but I havent told the code where to locate it...do i need to? where at? where should I placed the file?

    I tried using HMAC but keep getting a null boolean as a return value when using the API youtube functions all the time...

    Will appreciate any help

    thank you

    ReplyDelete
  57. I think iI figured out how to set the file path. I made my google_consumer_secret the path to the key file...but now I get :Undefined index: oauth_token.....

    ReplyDelete
  58. Yes you have to tell it the file location of your pem file. This is done by setting the 'secret' value of the parameter array you pass into the library when it is loaded.

    Check out the example code in the wiki for references.

    ReplyDelete
  59. Have you gotten an access token using google_oauth? You can't use the youtube API until you get an access token.

    ReplyDelete
  60. Well, using HMAC I get to do the youtube_request and the youtube_access all right. I got my aouth_token and the oauth_token_secret from the youtube validation page as expected, I store these at a raw text file for further access...

    But when trying to execute one of the youtube API functions I always get a false boolean in return (no Youtube data at all). I have a youtube developer key wich I´ve been using for other java projects successfully.

    For the RSA method, I still get Undefined index: oauth_token.

    Still, this power library does more than what i need at this moment in my development, will try a non auth solution this time.

    thank you very much for your great interest in helping other. Really appreciate your time..sure will be back sometime soon.

    Greetings

    ReplyDelete
  61. If you have oauth data from youtube then you are really close make sure you are url encoding both the token and secret before you pass them to the library like this

    $params['oauth']['access_token'] = array('oauth_token'=>urlencode('token'),'oauth_token_secret'=>urlencode('secret'));

    Also not all the methods require oauth authentication the "getTopRatedVideoFeed" only requires an api key.

    ReplyDelete
  62. 2 quick questions...

    Will this work on CI version 2?

    Also, I took a quick look and don't see any way to query Youtube by keywords, is this correct?

    Good job!

    ReplyDelete
  63. It does work with CI 2. I does not currently support keyword search but it shouldn't be too difficult to add.

    ReplyDelete
  64. Just wanted to update everyone and say that I added keyword searching. Check out the library on github for the latest version.

    When you call the getKeywordVideoFeed method you will pass an array of words you want to search for. NOTE: You should make sure you URL encode all values of the array.

    ReplyDelete
  65. Jim, ran into some issues pulling in the insight node with your library, found a missing parameter in the header array:

    'GData-Version'=>'2.0'

    this will allow you to return the insight link

    ReplyDelete
  66. Thanks allibubba!

    I added that to the library and made a constant to make it easier to specify which version you want to work with.

    ReplyDelete
  67. Hi, Jim.

    Your Youtube class looks great, thanks!

    ReplyDelete
  68. Jim - just wondering why you don't use =& when calling get_instance() in the constructor - only thing I might suggest when looking through this. Very good work!

    ReplyDelete
  69. Oh, that's just a minor mistake.

    It really doesn't matter though, because we are only using CI to load a helper and aren't dependent on the state of the parent CI instance.

    Assigning by value instead of reference might make the memory footprint a bit larger but wont do anything beyond that.

    I will fix it. Thanks for pointing that out.

    ReplyDelete
  70. Hi. I need the thumbnail for the videoId. But the function not works,
    $entry=$this->youtube->getVideoEntry('4XpnKHJAok8');
    $thumbnailUrl = $entry->mediaGroup->thumbnail[2]->url;
    Can u please get me solution.

    ReplyDelete
  71. The youtube api does not return PHP objects. It returns XML and it is your job to parse that XML into an object.

    Try echoing the value of $entry and you should see what I am talking about.

    ReplyDelete
  72. You specify that as the apikey array value.

    ReplyDelete
  73. Hey Jim.
    I've been looking your code and some questions came up to me.
    I'm using code igniter 2.0 and i wanna use the youtube api.
    Looking to your class, i see that you just implements the OAUTH method for authentication, and i need the ClientLogin method.
    Do you know any way to aply this method into codeigniter? Or even anyone has implemented it so far?
    'Cause google code has a version of it for zend, maybe someone has translated it to codeigniter.
    If you have something, let me know.
    Thank you so much.
    Cheers.

    ReplyDelete
  74. Paulo,

    Unfortunately as far as I know the only thing that will handle ClientLogin is Zend. You can try to edit the authorization portion of my library to perform ClientLogin (I don't think it would be too difficult).

    What you would want to do is get a ClientLogin token, then build the HTTP header for your request and send your request using the _connect() method.

    If you are uncomfortable with that or under a tight deadline then you should probably go with Zend.

    ReplyDelete
  75. Jim,
    Thanks for the reply,

    I do not think is the best solution, but I'm loading the Zend Gdata classes in CodeIgniter. Can bring me into trouble later?

    ReplyDelete
  76. Out of curiosity, why did you chose to handle the API requests with streams as opposed to CURL. I've noticed that none of the libraries available actually work with CURL when making API requests, but no one seems to know why. ...

    Thanks! (Great work by the way!)

    ReplyDelete
  77. I modeled it after how Zend was communicating with the API. I didn't put much thought into it beyond that. It could be an advantage though, as not everyone has the CURL module installed.

    You will notice that all of the other API libraries I wrote after that use CURL. Doing so makes for a much cleaner implementation.

    I have thought about rewriting it to use CURL, but haven't gone further than that.

    ReplyDelete
  78. @Paulo Henrique

    Loading Zend into your project shouldn't be an issue. There is just more overhead because of all the code there, and you might not use more than 5% of it.

    It adds bloat to your project but it should work just fine.

    ReplyDelete
  79. Thanks for the response Jim. Let me know if you ever have any success getting it to work with CURL. My experience with the youtube API is that beyond beyond getting the oauth credentials, there seems to be something that curl does that youtube doesn't like when making an API request, and results in an Authorized Header error. I haven't been able to nail down yet what it is, but if I do I'll let you know.

    ReplyDelete
  80. Hi Jim. I have been looking at your code, and as a newbie, I am trying to implement it in codeigniter 2.03. But...alas...I'm doing something wrong here (I keep my api key in a config file...):

    public function getNewVideos()
    {
    $this->config-load('MYgoogleApiKey');
    $params['apikey'] = $this->config->item('youtube_api_key');
    $this->load->library('youtube', $params);
    $vid = $this->youtube->getVideoEntry('z56t-SUuUdk');
    var_dump($vid);
    }

    And it outputs

    string(7483) "http://gdata.youtube.com/feeds/api/videos/z56t-SUuUdk2011-11-07T12:35:18.000Z2011-11-09T16:44:11.000ZA todos mis amigos de Facebook[...]"

    What I see is that $vid is a string, and not a XML object. It does indeed hold the url pointing to XML though... Could you enlighten me about this?

    Thank you very much

    ReplyDelete
  81. One thing that can be confusing if you are viewing the response via a web browser, is that the browser will hide the XML tags. Try viewing the page source and you should see that all the data is there.

    ReplyDelete
  82. Thank you... that solved the problem :)

    ReplyDelete
  83. I’m trying to implement the YouTube API on my codeIgniter website. I followed your instructions Jim, thanks a lot.

    SO first of all, I needed to get the token secret with the request_youtube function, that function works perfectly, i’ve entered my consumer key and consumer secret from google and everything is just fine. So then I’m redirected to YouTube to authorize the YouTube access, but when I’m redirected to my site, I got 2 problems :

    The Url I got is :

    mywebsite%2Fexample%2Faccess_youtube=&oauth_verifier=6wbPcxlOAmYQwaDU7HIhz38B&oauth_token=1/6Nfchy_nM-j4vxQzhAWTuc1J20L02bBNdcZasQP-e0s

    and codeIgniter doesn’t understant the “%2F” and doesn’t redirect me to the example/access_youtube function, If I correct it manually, then I go to my access_youtube function with my parameters but here is second (fatal) problem I got :

    First, here are my two variables I got from the url :

    oauth_verifier=6wbPcxlOA************
    &
    oauth_token=1/6Nfchy_nM-j4vxQzhAWTuc1J20L02b***********

    yet, on the google Oauth Playground (that you’ll be able to find here : http://googlecodesamples.com/oauth_playground/) I’m supposed to get something like that :

    oauth_token=1/P9o-yO1Czz1q67pvPm**********************iMI&
    oauth_token_secret=CzeSat342hO5HzswJEq94ZPH&oauth_callback_confirmed=true

    so that mean I didn’t get the same variables ... why ? how could I get thoses variables instead of the one I got ? Are they the same ? I guess there is a link between the oauth_token-secret and the oauth_verifier, and you explained it on the googlehelper but I didn't really understand ..

    Second, I got several errors in my access_Youtube function, CI tell :

    -Message: Undefined index: oauth_token
    -Message: Undefined index: oauth_token_secret

    so it doesn’t get informations from the header ... I don’t really know how to do so, I’ve followed your instructions but I’m a kind of stuck there....

    But Thank You so much for all your efforts, we are learning (here in franch engineering school) how to implement youtube on CI thanks to your helpful code:)

    ReplyDelete
  84. I am trying to investigate what you described, but google is not wanting to cooperate at the moment.

    Just based on what you described, it sounds like the url to your site is being urlencoded twice.

    Would you mind posting the array you get back from the youtube->get_request_token call? Also could you post the code for your request call?

    ReplyDelete
  85. Hi Jim,

    I'm having trouble getting this working. When youtube redirects, I get a 404 error because of their querystring.



    http://projecthivemind.com/example/access_youtube?oauth_verifier=(mumbo)&oauth_token=(jumbo)

    Any help?

    ReplyDelete
  86. Hi Jim, thanks for your response, so I went a little bit further in your code, when I come to my access_youtube function, which is exaclty the same than in your Oauth for Google example with HMAC, I got several error messages :

    Undefined index: oauth_token

    Undefined index: oauth_token_secret

    which refer to the lines $this->session->set_userdata ...

    and, after each error message I get :

    Message: Cannot modify header information - headers already sent by (output started at /homepages/7/d390075755/htdocs/system/core/Exceptions.php:170) Filename: libraries/Session.php Line Number: 671

    which, I think is linked with the previous error message.

    so I tried that in the constructor :

    class Example extends CI_Controller
    {
    private $CI;
    public function __construct()
    {
    parent::__construct();

    $this->CI =& get_instance();
    $this->CI->load->library('session');

    $oauth = array();
    $oauth['oauth_token_secret']='';
    $oauth['oauth_token']='';

    }
    just before my function but it doesn't do anything .. and I'm affraid I'm gonna loose my session variables stock by google ... isn't it ? Do I get my tokens from google ?

    Though, I still have to replace the “%2F” by "/" between the two function (once I'm redirected after Authorizing my app) ..

    Thanks for your help :)

    ReplyDelete
  87. okay let me give you some info :
    the lines the error message refers to are :

    $this->session->set_userdata('oauth_token', $oauth['oauth_token']);
    $this->session->set_userdata('oauth_token_secret', $oauth['oauth_token_secret']);


    When I come back from google user Authorization, my URL looks like :

    http://s390075769.onlinehome.fr/index.php?%2Fexample%2Faccess_youtube=&oauth_verifier=y4ugVOWDA2n39c9dgcOoEbdK&oauth_token=1%2FwNz--QE6Ra-oYQpfppgtu6YCJMvITBzmzCD1OTEmvbw

    And after I modify it manually it looks like :

    http://s390075769.onlinehome.fr/index.php?/example/access_youtube?oauth_verifier=kv9LNd0xS*******QbkQA7V&oauth_token=1%2FArAB3********rzY2YeGbz3-8czpZ7TiNIuGP6U6lwgnKWXE

    ReplyDelete
  88. @zero-I it sounds like you and @Miles might be having a similar issue.

    I have been trying to reproduce this issue but I am successfully redirected every time.

    Try this:

    Change this line in the get_request_token method of google_oauth:


    $auth = build_auth_array($baseurl, $this->_consumer['key'], $this->_consumer['secret'], array('oauth_callback'=>urlencode($callback),'scope'=>urlencode(self::SCOPE)), $this->_consumer['method'], $this->_consumer['algorithm']);


    To this:


    $auth = build_auth_array($baseurl, $this->_consumer['key'], $this->_consumer['secret'], array('oauth_callback'=>$callback,'scope'=>urlencode(self::SCOPE)), $this->_consumer['method'], $this->_consumer['algorithm']);


    What seems to be happening is that when you specify the callback URL in the request step, it is being urlencoded twice. I can't really say why that is happening without looking at your full code set. It could be some kind of configuration issue or maybe even an issue with the google server you are contacting.

    When the google_oauth library contacts the google server to get a request token, it passes the callback URL to the server so that later when you redirect to google for authorization it knows where to redirect back to your server.

    I will experiment and see if removing that urlencode works on my end. If it does then I will update the library.

    ReplyDelete
  89. Removing the urlencode on my end caused an invalid signature error. However I did notice that I don't have any logging in this library for debug purposes, and also I am not setting the curl headers as properly as I should.

    Try getting the updated version and see if that fixes your issue. I don't think it will because the changes I believe are unrelated, but at least you will be able to turn on logging and get a better picture of what might be going on.

    ReplyDelete
  90. Well, I've spent all night and gotten nowhere. I started with a fresh install, and couldn't even get to the point it was breaking at before.

    Jim, would it be possible for you to post an easier starting point, like I just drop in my key files, change the config, and it works? Or is that what I'm playing with? Maybe it's my lack of sleep, but this feels hopeless.

    ReplyDelete
  91. Er, by fresh install, I mean I started with a fresh CodeIgniter install, then installed sparks from command line, then your youtube spark and example controller.

    ReplyDelete
  92. Thanks Jim for your efforts and your reactivity, Well 3 things :

    First, I've been changing the code as you recommended me, it didn't work, in fact, my request function returned me an error like :
    Undefined index: oauth_token for concerning the line : "$redirect = self::SCHEME.'://'.self::HOST.self::AUTHORIZE_URI."?oauth_token={$resarray['oauth_token']}"; "

    and Undefined index: oauth_token_secret for line : " else return array('token_secret'=>$resarray['oauth_token_secret'], 'redirect'=>$redirect); "




    but I've been trying to solve my problem and I when to point 2 :

    for the access_youtube function, I had, as written before :

    Undefined index: oauth_token

    Undefined index: oauth_token_secret

    which refer to the lines $this->session->set_userdata ...

    well, I find out that this pb was due to a third %2F that I didn't see right after the oauth_token=1 , and when I modify it manually once again, well I had my tokens ... Yeah !

    So, considering that I'm gonna work on the urlencode function (if you have other ideas do not hesitate :) ) I've been further, and tried the youtube_auth function (because I want the user to be able to upload a video on YouTube), and it returned my that :

    http://gdata.youtube.com/feeds/api/users/docfunky7/uploads2011-12-09T14:02:38.182Zhttp://www.youtube.com/img/pic_youtubelogo_123x63.gifdocfunky7http://gdata.youtube.com/feeds/api/users/docfunky7YouTube data API1110http://gdata.youtube.com/feeds/api/videos/d9eNTNYzbFo2011-12-09T10:49:34.000Z2011-12-09T10:49:54.000ZyesInvalid file formatTest Direct Uploading.DocFunky7http://gdata.youtube.com/feeds/api/users/docfunky7PeopleTest Direct Uploading.testTest Direct Upload

    so ... what does that mean ? is that normal ? I've tried then the direct_upload function but I had about the same message ... and no video uploaded (I guess, so far no video url returned to me ..) could you enlight me on those points please :)

    well lets terminate by point 3 :

    I've seen some people, including me, some school partners and few people on stackoverflow that had the following problem : Call to a member function library() on a non-object in the request function, and I advise them to modify the construct to :

    public function __construct()
    {
    parent::__construct();

    $this->CI =& get_instance();
    $this->CI->load->library('session');

    }

    and it worked, there is a question here on stackoverflow : http://stackoverflow.com/questions/8411456/codeigniter-youtube-data-api-by-jim-saunders/8447690#8447690

    last but not least, maybe should you advice the developer that he has to be sure that his server has turned the "allow_url_fopen" to "on", cause I had a pb because of that on my server.
    Don't see any kind of complaints here, that's simply the problem I had trying to implement your library. I'm just trying to give you some of the solutions to problems I got as far as you're trying to help me :)

    ReplyDelete
  93. @zero-I please post your controller code. Also I just updated the google_oauth library. You can turn on logging by setting the DEBUG flag to true in the google_oauth.php file. That will give you a better indication of what might be going on.

    The provided example controller is about as simple as this can get. However I should note if you are using sparks you will have to add the line that loads the spark before loading the library. Other than that, all you need is your API key and your oauth credentials from google.

    Don't give up hope. I am more than happy to continue to work with you until we figure out the issue.

    Post your full controller code to pastebin and then send me the link. (Make sure to remove your API keys and Oauth keys)

    ReplyDelete
  94. When I mean putting "allow_url_fopen = On " I refer to the "Warning: file_get_contents() [function.file-get-contents]" error message

    ReplyDelete
  95. @Miles I am glad you are making some progress. Please turn on logging (set the DEBUG flag in the youtube library and the google_oauth library to true). That will log all requests and responses, in the default PHP error log. Doing that should give you a much better picture of what is going on.

    Thanks for the heads up about the stackoverflow question I was unaware that it existed. The problems that it described were related to an issue with the example controller. I have updated the GIT repo with a fix. You shouldn't need to store your CodeIgniter instance in a variable.

    allow_url_fopen is on by default in PHP and must be explicitly turned off. Meaning it probably wont happen to many other users. However if you provide the full warning text, I will update this post with the fix.

    ReplyDelete
  96. is that normal my post doesn't appear anymore ? Okay I m gonna work on it, so you say there is an up to date version of your example ?

    ReplyDelete
  97. I'm not sure why that happens there have been other comments that don't appear as well. It might have to do with posting code in the comment.

    I still get an email with the full comment so even if it doesn't appear on the site I will still be able to answer your questions.

    ReplyDelete
  98. They were getting caught in the spam filter.

    ReplyDelete
  99. Whew, got it going! Thank you, Jim!

    My initial problem was an old version of CodeIgniter.

    My next problem was replacing my "secret" string with the path to my key file in the function request_youtube(). Now, I'm not sure why that didn't work - wasn't I supposed to?

    When I replace
    $params['secret'] = 'jiBBaJabb4';
    with
    $params['secret'] = '/path/to/auth/gkey.pem';
    it gives the error
    Message: Undefined index: oauth_token
    Filename: libraries/google_oauth.php

    Am I doing something wrong? Do you need more data?

    ReplyDelete
  100. Oh... that was presumptive of me... I don't yet know what my initial problem was.

    ReplyDelete
  101. Make sure you aren't specifying HMAC as your signing algorithm if you want to use a pem file. A file path only works for RSA signing, and admittedly I haven't tested RSA signing in a long time since most people use HMAC.

    ReplyDelete
  102. You were right.

    I switched it, and whether using secret string or pem file, get_request_token() returns a string:
    https://www.google.com/accounts/OAuthAuthorizeToken?oauth_token=1/j1bb4JAbbawtlkjqelwk

    ReplyDelete
  103. Also, seems to have been Phil Sturgeon's MY_URI class that was causing my initial problems. I got rid of it and don't think it broke anything.

    ReplyDelete
  104. Glad to hear you got things running.

    I will investigate the MY_URI library you are talking about and see if I can determine where the problem might be.

    ReplyDelete
  105. Well, it's... not running, though.

    get_request_token() is supposed to return an array.

    It works properly with secret string and HMAC, but when I switch to RSA, it has the same result whether secret string or key file path - a string like above.

    ReplyDelete
  106. Got it working. My apologies, I was whining before debugging. I didn't realize I had to change the controller around after switching to RSA.

    Thanks you for your help. I was ready to give up!

    ReplyDelete
  107. Hmm, well I've tried several things to get my URL correctly so that it redirect me to the access function, but nothing seems to work. Are you sure I shouldn't try a urlencode before the access function ? Because even if I put an urlencoe in the access function, I can't get to that function, so maybe I should try to put the urlencode function in the request function ?

    What make you think that the problem comes from a second urlencode in excess ? I rather think there is maybe one missing somewhere isn't it ?

    I found the DEBUG flag in the youtube labrary but not in the oauth , I just added it but is that normal ? I didn't see anything since I can't reach to youtube_auth function and then direct upload because, I don't know why I get again the "Undefined index: oauth_token (and secret also)" error message ... grrr

    ReplyDelete
  108. .... when I try to reach the access_function by correcting the URL manually

    ReplyDelete
  109. okay I've tried several times and it seems to work one time on two, so I had about the same message when I've reached the youtube_auth function, it returned me that on my browser (not in the url) : http://gdata.youtube.com/feeds/api/users/docfunky7/uploads2011-12-10T18:19:50.471Zhttp://www.youtube.com/img/pic_youtubelogo_123x63.gifdocfunky7http://gdata.youtube.com/feeds/api/users/docfunky7YouTube data API3110http://gdata.youtube.com/feeds/api/videos/mb_vl6ZYO3w2011-12-10T17:36:29.000Z2011-12-10T17:36:55.000ZyesInvalid file formatTest Direct Uploading.DocFunky7http://gdata.youtube.com/feeds/api/users/docfunky7PeopleTest Direct Uploading.testTest Direct Uploadhttp://gdata.youtube.com/feeds/api/videos/kDN3Gjr8WGw2011-12-10T17:36:29.000Z2011-12-10T17:36:55.000ZyesInvalid file formatTest Direct Uploading.DocFunky7http://gdata.youtube.com/feeds/api/users/docfunky7PeopleTest Direct Uploading.testTest Direct Uploadhttp://gdata.youtube.com/feeds/api/videos/d9eNTNYzbFo2011-12-09T10:49:34.000Z2011-12-09T10:49:54.000ZyesInvalid file formatTest Direct Uploading.DocFunky7http://gdata.youtube.com/feeds/api/users/docfunky7PeopleTest Direct Uploading.testTest Direct Upload

    Still the same ..

    ReplyDelete
  110. Get the latest version of the library from github. It has logging set up in the google_oauth library.

    Go to pastebin and enter your entire controller. Then send me the link. Make sure you remove your oauth consumer key and secret.

    I will be able to debug much easier if I can see what might be going on.

    ReplyDelete
  111. Okay, I got the last GitHub version of your libraries/helper.

    So I restart from the begining, step by step the process this week end.
    First of all, do I need to do anything with the oaut_helper ? do I need to work with the auth headers (like your example on CI wiki : "Authorization: OAuth oauth_version="1.0", oauth_nonce="3b... ") or your library does it automatically (in my mind it does, but I just want to verify)
    Then, you've asked me to " log all requests and responses, in the default PHP error log", hmmm I don't see what do you want me to do, something like echo $all_variables_in_the_code to see there is in it ? Do I have to do something in particular for the default PHP error log ?
    And yeah, I don't have sparks, I didn't know what that was, seems cool !

    So here is my controller :
    http://pastebin.com/0uX0sdbP

    Thank You !

    ReplyDelete
  112. Setting the DEBUG flag to true will write a bunch of things to the PHP error log. You don't need to log anything yourself. I made a few changes to your controller namely:

    If you load a library with $this->CI then you should call the library you load with $this->CI checkout the changes I made to the paste bin

    http://pastebin.com/UhsvutuQ

    ReplyDelete
  113. Hi Jim,

    I read through all your wiki's but I am still having some trouble. I wonder if you could assist me in this matter.

    Here is my controller:
    http://pastebin.com/BWZGLsa9

    and running request_youtube gives me the following errors:

    ----------------------------------------------------------
    A PHP Error was encountered

    Severity: Warning

    Message: openssl_sign() [function.openssl-sign]: supplied key param cannot be coerced into a private key

    Filename: helpers/oauth_helper.php

    Line Number: 91

    ----------------------------------------------------------

    A PHP Error was encountered

    Severity: Warning

    Message: openssl_free_key() expects parameter 1 to be resource, boolean given

    Filename: helpers/oauth_helper.php

    Line Number: 92

    ----------------------------------------------------------

    A PHP Error was encountered

    Severity: Notice

    Message: Undefined index: oauth_token

    Filename: libraries/Google_oauth.php

    Line Number: 104

    ----------------------------------------------------------

    I think the last error was posed by zero-I but there wasn't any recommendation on how it was solved.

    I am trying to use RSA signing, HMAC works, but i prefer RSA...

    I have the file path as $config['google_consumer_secret'] = 'path/xxx.pem';
    Sorry for the trouble, thank you for your API and hardwork.

    ReplyDelete
  114. Your controller looks correct. Are you sure you are using a private key PEM file?

    When I generated my key files I had two PEM files one was the private key, and one was the public certificate.

    The easiest way to check which is which, is to open the file in a text editor. The private key will say "RSA PRIVATE KEY" and the public certificate will say "CERTIFICATE".

    You want to use the private key, you don't need the public certificate in this situation.

    ReplyDelete
  115. Ok, got it working! Thanks for the API and the help neil. Great addition to the codeigniter framework! :)

    ReplyDelete
  116. Hello.

    First of all, awesome work on this CI API and others.

    I just wanted to know how can I request the video feed from a particular playlist (I have the playlist id). Is my own playlist BTW.

    Thanks!

    ReplyDelete
  117. I just updated the library with a "getPlaylist" method that let's you pass in the playlist id and get back a list of videos.

    Let me know if there are any other features you would like to see added.

    ReplyDelete
  118. Hey Jim, great library man just great. I was wondering if you planning on making any json response version instead of xml?

    I have been testing the library for about a day and I would like to know if there is anyway for the getKeywordVideoFeed function to return the thumbnail for the videos returned?

    Please reply to my email if possible.

    ReplyDelete
  119. Alex, glad you like the library. You can get json returned from any call by specifying an alt parameters with value of 'json'. Read here for more info: http://code.google.com/apis/youtube/2.0/reference.html#Query_parameter_definitions

    I am not sure there is much you can do to get what you want from the getKeywordVideoFeed function.

    ReplyDelete
  120. It's weird that if I do an alt=json I can retrieve thumbnails and even do some nice filtering for what fields I need to retrieve but with xml I just can't get it to retrieve thumbnails.

    http://code.google.com/apis/youtube/2.0/developers_guide_protocol_partial.html#Partial

    ReplyDelete
  121. Nathanael ShermettMay 7, 2012 at 8:40 PM

    Hey there!

    First off, let me say THANK YOU for this! This library is excellent--I love it! However, I appear to have encountered a serious glitch.

    When performing a direct upload, if the file is really big, it won't upload all the way. It gets truncated, but the directUpload() function doesn't return a failure. Instead, it uploads to YouTube successfully, but the entire video is not there. I am attempting to upload videos that should be in excess of an hour long, but once uploaded, they only appear to be about 10-20 minutes long. Watching the end of the video confirms that they have, indeed, been truncated.

    What's up? I'm guessing that some sort of timeout, or memory max-out, is occurring. Perhaps you could modify the library to avoid this problem? I'm sure it would help many, many people.

    Thank you!

    ReplyDelete
    Replies
    1. Are you sure YouTube will let you upload an hour long video? Direct upload doesn't really use your server so there aren't any limitations there. All the library does is give you a form action to upload directly to a YouTube server.

      Delete
    2. Nathanael ShermettMay 10, 2012 at 2:19 PM

      Unfortunately, I just confirmed yes, I can upload the complete video via the YouTube website.

      Uploaded via YouTube website:
      http://www.youtube.com/watch?v=tVdFkqs__JQ

      Uploaded via CodeIgniter Library:
      http://www.youtube.com/watch?v=Vsi6lHQi3kk

      Any idea why this might be? I'm willing to provide you with my code, but I haven't made any modifications to the directUpload() function, which is where I assume any issues would be located. Also, note that when I upload the same video multiple times with the CodeIgniter library, each upload has a different duration, +/- ~3-4 minutes. This is another thing that was leading me to believe there's some memory issues. I also just checked, and confirmed that the file sizes I am calculated are indeed consistent, and not subject to error. The only explanation I can find is that the directUpload() function is screwing up somewhere.

      Delete
    3. Looking back at the code I confused directUpload and getFormUploadToken so you could very well be correct.

      directUpload does store the video contents in a variable for a short time so it could be that the video is simply too large for PHP to handle.

      Try this updated version:

      http://dl.dropbox.com/u/13081549/Youtube.php

      I altered the code so that the call to file_get_contents goes directly into the stream and is never stored in a variable. If you are still having issues then I can rework it further to not use file_get_contents at all.

      Also this version has the debug flag turned on so monitor your php_error log for output from the library that will help you pin point where an issue might be.

      Delete
  122. I do not have access to the PHP error log, due to the server configuration I am working on.

    Regardless, it still doesn't working. Looking over the youtube.php file, here are a couple things I noticed that might be causing problems:

    ---
    Line 479: filesize() reads the entire file into memory to calculate the file size, so this could be hurting things.

    Line 493: still using file_get_contents(), which reads the entire path into memory.

    Lien 496: I'm not sure what, exactly, this is reading, so it might or might not be a problem.
    ---

    Try looking at the following resources to see a possible solution. I tried modifying the youtube.php file into something similar, but wasn't able to figure out how the YouTube API actually works. Thanks for your support!


    http://stackoverflow.com/questions/5249279/file-get-contents-php-fatal-error-allowed-memory-exhausted

    http://teddy.fr/blog/how-serve-big-files-through-php

    ReplyDelete
    Replies
    1. You have to know the filesize of the video because you have to put that in the request. I can alter the library to not use file_get_contents and instead grab the video a few chunks at a time and write that to the stream.

      Line 496 is reading the response sent back from the youtube server to determine if the request was successfully sent.

      Without access to the error logs I can't provide too much assistance. Try increasing the memory usage of PHP. You can do that in the htaccess or in the php.ini file. Afterwards if you are able to upload more then that will indicate that it is definitely a memory issue.

      Delete
  123. I'll try to gain access to the error log.

    Until then, I understand that we need to know the filesize of the video. However, that's accepted as a parameter for the function--I already wrote up a way to calculate the file size without reading it to memory, and I serve that to the directUpload() function. Why do you need to do it inside the actual function as well?

    ReplyDelete
    Replies
    1. The standard version of directUpload doesn't take filesize as a parameter. If you are specifying it in the meta data that is different than specifying it in the request.

      Try the new version I just put on dropbox. It uses chunked uploading and also lets you optionally specify the filesize.

      http://dl.dropbox.com/u/13081549/Youtube.php

      Delete
    2. New version doesn't work either. Here's what I'm finding in the PHP error log:


      [14-May-2012 11:03:28] POST /feeds/api/users/default/uploads HTTP/1.1
      Host: uploads.gdata.youtube.com
      Connection: close
      User-Agent: CodeIgniter
      Accept-encoding: identity
      X-GData-Key: key=AI39si5DI3aiqkvc22HfPJZP_NoFUVfMLywiU1pZdpZj1FiqCjFF-YNgIIeo4yNjuo6hjvq6UO7bWu-ldc8ng40kA27LTHL5pQ
      Authorization: OAuth oauth_consumer_key="myflock2.com", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1337011408", oauth_nonce="cce507952981439121d2d08f24e56e83", oauth_version="1.0", oauth_token="1%2FkSK5VmH2pacnxN7alEmuuR5x1yJAumLUzYPHDo7Hns4", oauth_signature="70CoyodfKn6vGIFknTGwGgpaiVo%3D"
      GData-Version: 2.0
      Slug: temp.tmp
      Content-Type: multipart/related; boundary="6aFflwuP"
      Content-Length: 794836848

      --6aFflwuP
      Content-Type: application/atom+xml; charset=UTF-8





      TITLE





      Nonprofit


      TAGS



      --6aFflwuP
      Content-Type: video/mp4
      Content-Transfer-Encoding: binary

      Delete
    3. Sorry, XML tags were stripped. That middle part is just the XML request.

      Delete
  124. I just ran it again, and found a new error:


    [14-May-2012 11:19:15] YouTube library received bad response:



    It ends right there. No explanation of what the bad response actually is was provided.

    ReplyDelete
    Replies
    1. Youtube must be returning an empty response. The library usually includes the response text in that message.

      Why don't you email me, that way you don't have to worry about text getting stripped out. My email address can be found here: http://jimsaunders.net/contact (I don't want to post it directly to prevent spam)

      Would it be possible for you to give me the video so that I might test with it?

      Also just as a sanity check:

      You have been able to upload a smaller video successfully.

      You increased the PHP memory size and saw more of the video uploaded to Youtube as a result.

      Delete
  125. I did much more testing, and finally figured out what was going wrong. There was a combination of memory issues (which your chunked file upload fixed) and server timeouts. (which I fixed on my own) Thanks for your help! I highly recommend you commit the chunked upload to the official release, as that is definitely an important feature to have.

    Thanks!

    ReplyDelete
    Replies
    1. Great work! I was waiting to hear back from you before I made any commits. I will get those in ASAP. Thanks for your patience.

      Delete
  126. Would you be able to implement a means of checking the maximum upload length for an authenticated user? I know the YouTube API supports this, and it'd be very useful for checking uploads before actually uploading any files.

    ReplyDelete
    Replies
    1. I certainly can if it is truly available via the API. However I wasn't able to find the documentation on it. Could you link to the appropriate page in the API docs?

      Delete
    2. Done! :)

      https://developers.google.com/youtube/2.0/reference#youtube_data_api_tag_yt:maxUploadDuration

      Delete
    3. That's why I couldn't find it I was looking in the wrong guide. This is already available. It can be found by calling the getUserProfile method.

      Note that according to the docs if the maxUploadDuration tag doesn't exist then it's unlimited. So it's not guaranteed to exist.

      Delete
    4. Using your code, how do we access the individual tags? It isn't clear, and the response returned by the getUserProfile method is just a long string--not an array or similar.

      Delete
    5. The library wont parse the response for you. You have to do that in your own code. The default returned by youtube is XML so you can use any XML parser to get the values you need.

      I also just updated the library because I noticed that the getUserProfile method did not allow parameter passing. If you grab this latest version then you can pass in the alt parameter and specify which return type you want.

      A list of the different parameters to pass can be found here: https://developers.google.com/youtube/2.0/developers_guide_protocol_api_query_parameters

      Delete
  127. Hi Jim, thanks for this library, im try to do the delete method but i dont know what is wrong, i send the video parameter to the method but i dont recieve any error

    Thanks

    if($this->_access !== false)
    {
    $uri = "/{$this->_uris['USER_URI']}/default/uploads/{$video_id}";
    $header = "DELETE {$uri} HTTP/".self::HTTP_1.self::LINE_END;
    $url = self::URI_BASE.substr($uri, 1);
    $extra = "Content-Type: application/atom+xml".self::LINE_END;
    $extra .= "GData-Version: 2.0".self::LINE_END;

    $fullrequest = $this->_build_header($url, $header, $extra, 'DELETE');

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

    fclose($handle);
    $handle = null;

    return $output;
    }
    return false;

    https://developers.google.com/youtube/2.0/developers_guide_protocol#Updating_and_deleting_videos

    ReplyDelete
  128. Hey Jim :
    I am wondering. Can this api be used to update metadata fields or the Privacy Setting (Pirvate, Public, Unlisted) after the video has been uploaded. This would allow for the ability to prep videos as Private or Unlisted then release them when ready.

    ReplyDelete
    Replies
    1. I wouldn't think that would be too difficult to add. Just look at some of the other post methods and check the YouTube documentation for the correct route to use.

      Delete
    2. I just came across this article last night. This is just great work. We built a similar but not as clean solution like this some time back. I will probably be looking this at this in about 3 weeks. Thanks and I will keep you up to date if I add anything.

      Delete
  129. Hi,
    I'm using direct upload and it works fine but I don't know what to do with the returned data.
    I'm getting this: tag:youtube.com,2008:video:OYUr2cdSpIs2013-01-17T18:18:43.000Z2013-01-17T18:18:43.000Z2013-01-17T18:18:43.000Zyesvelcrodesignhttp://gdata.youtube.com/feeds/api/users/velcrodesigndjjAQJa310ymM8ho0rAT6wPeoplevelcrodesignTest Direct Uploading.testyoutubeTítulo do vídeo2013-01-17T18:18:43.000ZUCdjjAQJa310ymM8ho0rAT6wOYUr2cdSpIs

    How can I get only the video ID?

    Thanks

    ReplyDelete
    Replies
    1. It looks like your result is getting screwed up by the blogspot comment formatting. I'll attempt to answer your question anyway.

      By default YouTube will return XML on requests. It's your responsibility to parse that and take what you need from it. There are some methods of the library that let you specify the return type (xml, json, etc.) but unfortunately directUpload isn't one of them. So just parse the xml to get the video id.

      Delete
  130. This doesn't have as much to do with your YouTube library as authenticating other Google services with google_oauth and your helper.

    I'm trying to get Google Drive authenticated using your tools, and I think I'm 90% there (I'm also using your Dropbox library - it's great!).

    I get through the request and the access functions and it looks like oauth_token and oauth_token_secret session values are being set.

    Where I fall flat is trying to use the access tokens to authenticate a Google client through Google's php library.

    Here's what I have:

    >>>>
    require_once 'google-api-php-client/src/Google_Client.php';
    require_once 'google-api-php-client/src/contrib/Google_DriveService.php';

    $client = new Google_Client();
    $client->setClientId('676928059564.apps.googleusercontent.com');
    $client->setClientSecret('mAaujhYdxKI5HMbdPo1fTlKc');
    $client->setScopes(array('https://www.googleapis.com/auth/drive'));

    $service = new Google_DriveService($client);

    $client->setAccessToken(urlencode($this->session->userdata('oauth_token')));
    <<<<

    When I run it, I get a 'Could not json decode the token' error.

    Any ideas?

    Thanks a ton!

    -Paul :)

    ReplyDelete
    Replies
    1. Paul,

      I would not recommend using my google oauth library in new code. The reason being because google has told me that they intend to deprecate the OAuth 1.0a spec (what my codes uses) in favor of the newer OAuth spec. I would imagine google has provided some authentication methods in their own code you should use that.

      Also since you posted your client id and secret please make sure you change those as keeping them secret is paramount to the security of your application.

      If you wish to continue using my code I would recommend setting the DEBUG flag in the google oauth code to true. That will write all of the requests and responses sent to the google servers into your default PHP error log. Looking through that should give you a better indication of what might be going on.

      Delete
    2. Doh! I was totally going to delete the id and secret and someone popped into my office and I promptly forgot!

      Thanks for the feedback... I've been working with Google's Oauth 2.0 code, but there's a pile of craziness - the access token comes in with a / in it, which CI doesn't like, plus it plays hell with BitAuth...

      But I'll get there!

      -P

      Delete
  131. Really nice blog. I downloaded the code but unfortunately I am getting an error when uploading direct video.

    ReplyDelete
  132. This comment has been removed by the author.

    ReplyDelete
  133. Hi Jim,

    I am getting an 'Invalid Token' response upon calling request_youtube() and I don't know why. Please help. Thanks man.

    ReplyDelete
    Replies
    1. You should set the DEBUG const in the library to true. That will write out the request and response data to your default PHP error log. You can use that to figure out where the problem is. I can't really give you more assistance than that without seeing your code.

      Delete
    2. Thanks for the reply Jim, I finally got the token working. There is another problem that I encountered, this is when I use the direct_upload().. specified the video path and type then called it directly in the browser and returned the ff error:

      Severity: Warning

      Message: filesize() [function.filesize]: stat failed for http://mysite.com/video/Wildlife.wmv

      Filename: libraries/Youtube.php

      Line Number: 498

      ---------------------------------------------

      Severity: Notice

      Message: fwrite() [function.fwrite]: send of 7050 bytes failed with errno=32 Broken pipe

      Filename: libraries/Youtube.php

      Line Number: 542
      ---------------------------------------------

      Any idea on how can I avoid this? Thanks Jim.

      Delete
    3. This is not a problem with the library. PHP can't get access to the file. I suspect because you entered a URL instead of a file path.

      Delete
    4. You are correct, my bad. Thanks a million master Jim! Great work you got here.

      Delete
  134. hi Jim I am using direct upload function but I am not getting any response.Can you please help me on that. I am using 45kb 1 seconds video which is MP4

    ReplyDelete
    Replies
    1. The video you are uploading isn't very important. Set the DEBUG const at the top of the library to true and see if that gives you a better idea of what is going on.

      Delete
    2. Hi Jim thanks for your reply but I tried that also still it doesn't show anything. In my controller 'direct_upload' I used :

      var_dump($this->youtube->directUpload($videoPath, $videoType, $metadata));

      to see what I get from library but it prints bool(false).

      I don't understand what is the error. All the authentication are working fine but direct upload does not give any result. Please help me on this

      Delete
    3. Turning the debug flag on does not change the output. It writes detailed information to your php error log. Please check that and you might get a better idea of what is going on.

      Delete
    4. I got following message in error log :-

      [29-May-2013 12:58:00]

      --HBxvORhr--


      [29-May-2013 12:58:00] YouTube library received bad response: HTTP/1.1 400 Bad Request

      Server: HTTP Upload Server Built on May 8 2013 16:51:19 (1368057079)

      X-GData-User-Country: IN

      Content-Type: application/vnd.google.gdata.error+xml

      X-GUploader-UploadID:

      Date: Wed, 29 May 2013 07:28:00 GMT

      Pragma: no-cache

      Expires: Fri, 01 Jan 1990 00:00:00 GMT

      Cache-Control: no-cache, no-store, must-revalidate

      Content-Length: 181

      Connection: close

      Delete
    5. According to the error you are getting from the YouTube API you are making a bad request. Make sure that the information you are sending is correctly formatted.

      Delete
    6. My function in controller is :-

      public function direct_upload()
      {

      $videoPath = $_SERVER['DOCUMENT_ROOT']."/resources/video/videoplayback12.MP4";
      $videoType = 'video/mp4'; //This is the mime type of the video ex: 'video/3gpp'

      $params['apikey'] = 'MYAPIKEY';
      $params['oauth']['key'] = 'AUTHKEY';
      $params['oauth']['secret'] = 'SECRETKEY';
      $params['oauth']['algorithm'] = 'HMAC-SHA1';
      $params['oauth']['access_token'] = array('oauth_token'=>urlencode($this->session->userdata('oauth_token')),
      'oauth_token_secret'=>urlencode($this->session->userdata('oauth_token_secret')));
      $this->load->library('youtube', $params);

      $metadata = 'Test Direct UploadTest Direct Uploading.Peopletest';
      var_dump($this->youtube->directUpload($videoPath, $videoType, $metadata));
      }

      Delete
    7. I'm guessing the tags go stripped out of your XML meta data when you posted your comment. That would be the first place to look. Make sure that it is properly formatted and has all the required attributes.

      Delete
    8. See I am not changed anything on that direct upload function except of required key rest of the code is same as in the api. I am not able to format the code properly but I am trying to give you as much details :-

      -->entry tag start<--
      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"<--
      Test Direct Upload
      -->close media title<--
      -->media:description type="plain"<--
      Test Direct Uploading.
      -->close media description<--
      -->media category scheme="http://gdata.youtube.com/schemas/2007/categories.cat"<--People

      -->close media category<--
      -->media:keywords<--
      test
      -->close media keywords<--
      -->close media group<--
      -->close entry<--


      Code are same as api which I downloaded except the key and file path

      Please help me on this because I am trying since 5 days. Thanks in advance.

      Delete
    9. I ran tests on the library and had no problem performing a direct upload. Something has to be wrong on your end. Have you tried both a direct path and relative path to the video? Double check that your metadata xml is valid. You need to read the YouTube documentation to make sure.

      Delete
  135. Hi,
    i have tried using the codeigniter library provided. i can successfully used the exisitng example (*request_youtube) and upload the videos to youtube. but the problem is, it keep on asking permission to grant access the youtube app and i have to login everytime it did.
    Can anybody help me..pleeaaseee

    ReplyDelete
  136. Hi Jim,

    I'm attempting to use the direct upload but it keeps failing with this error "Message: fwrite() [function.fwrite]: send of 8192 bytes failed with errno=32 Broken pipe". The other api functions work fine so the tokens and authorization are good. In the php logs I see this error.

    "[07-Aug-2013 13:17:16 America/New_York] YouTube library received bad response: HTTP/1.1 400 Bad Request
    Content-Length: 39
    Date: Wed, 07 Aug 2013 17:17:16 GMT
    Server: HTTP Upload Server Built on Jul 24 2013 17:20:01 (1374711601)
    Content-Type: text/html; charset=UTF-8
    Connection: close"

    I double checked the metadata against what youtube requires and everything looks fine. The file path is working, I have tried both relative and absolute and get the same issue.

    Do you have any ideas what the issue might be.

    ReplyDelete
    Replies
    1. I just tested it out and was able to successfully upload a video. What does your implementation look like? Is the fwrite error coming from within the library?

      Delete
  137. yes, straight implementation of your library. Basically took the code you had in the example, inserted the correct token values and nada.

    ReplyDelete
  138. Set the DEBUG const in the library to true that will write the request header string to your PHP error log and let you see what what you are sending to youtube. What line in the library is the fwrite error coming from?

    ReplyDelete
  139. the error happens on line 546 of libraries/youtube.php

    "fwrite($handle, fread($filehandle, $chunksize));"

    this is the first error
    Message: fwrite() [function.fwrite]: send of 7050 bytes failed with errno=32 Broken pipe

    and then subsequent ones
    Message: fwrite() [function.fwrite]: send of 8192 bytes failed with errno=32 Broken pipe

    until it hits the end of the file.

    It seems odd the first chunk size is 7050 and not the full 8192

    ReplyDelete
    Replies
    1. Which version are you using? In the current stable 1.1.1 tagged version line 546 is a PHPDoc comment line. Make sure you pull from the latest tagged stable release (1.1.1)

      Delete
  140. updated to 1.1.1, getting same error on line 537

    ReplyDelete
    Replies
    1. Did you set the DEBUG flag? What does the request look like that you are sending to youtube?

      Delete
    2. Here is an example of the request I send. I removed a few things but left most of it in. It might not render correctly in the comment

      POST /feeds/api/users/default/uploads HTTP/1.1
      Host: uploads.gdata.youtube.com
      Connection: close
      User-Agent: CodeIgniter
      Accept-encoding: identity
      X-GData-Key: key=I_REMOVED_MY_GDATA_KEY
      Authorization: OAuth oauth_consumer_key="hereb4me.com", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1375904520", oauth_nonce="f1fdd6952ce9f030f2a29068c0f4ea96", oauth_version="1.0", oauth_token="1%2FhxFIBRH4MBDSIqly9Mor6tTv6kv9N0SgYoTiaUhIgWA", oauth_signature="w4BnCXyWRFG5zarrgdTQid%2FVcsQ%3D"
      GData-Version: 2.0
      Slug: test.3gp
      Content-Type: multipart/related; boundary="CD7iO5C4"
      Content-Length: 1768305

      --CD7iO5C4
      Content-Type: application/atom+xml; charset=UTF-8

      Test Direct UploadTest Direct Uploading.Peopletest
      --CD7iO5C4
      Content-Type: video/3gpp
      Content-Transfer-Encoding: binary

      BINARY_FILE_DATA
      --CD7iO5C4--

      Delete
    3. Yeah so the xml didn't render correctly but the rest of the request did and the XML looks exactly like what is in the example controller.

      Delete
    4. yes, xml was exactly yours, only change was some of the node values but structure and attributes were untouched, changed the path for the video back from relative to absolute and it worked. No clue why since it was reading the file on the fopen but regardless, it's working now. Huge thanks for the help.

      Delete
  141. Found this and thought nice, as do not want to play with Zend. I seem to not see the wood for the trees. Is there a way to grab the user details only? ID, username and such.
    Pardon my ignorance as am a part time coder for fun in my spare time.

    ReplyDelete
    Replies
    1. You could probably use the getUserProfile method to do that.

      Delete
  142. I would like to say thanks you.

    ReplyDelete
  143. Hi jim i am shiva kakileti,i am working with youtube api integration by using your codeigniter api library,

    while doing this ,i need to place

    $params['oauth']['key'] = 'ENTER YOUR GOOGLE CONSUMER KEY';
    $params['oauth']['secret'] = 'ENTER YOUR GOOGLE CONSUMER SECRET';
    $params['oauth']['algorithm'] = 'HMAC-SHA1';
    $params['oauth']['access_token'] = auth_token')),

    to get these i need to get oauth authentication,

    when i create oauth authentication it giving
    client id and client secret only

    how to get

    oauth key,oauth secret,accesstoken,acces secret

    please tell me

    Thanks in advance

    ReplyDelete