Accessing the You Tube API from C#


Welcome to my last post of 2009, I hope you all had a jolly good Christmas Holiday so far, I know I did and I had some well earned down time.

Recently there’s been a couple of questions on Lidnug about accessing the You Tube API.  I knew I had some proof of concept code kicking around, but it was no where near any use, so I dug out the You Tube docs and took a look at things.

Like many API’s these days the version of the API currently being used is http and xml orientated, and is quite easy to use.  Unfortunately there’s also a lot of options, some involving posts, some involving puts and others just being normal http gets.  To keep things straight forward, I’m just going to concentrate on the Search facility here, I’ll leave the rest to you to explore.  The actual API documentation can be found here I encourage you to read them fully once you’ve read through this article.

Since the API returns XML (Specificly an Atom feed) then it’s a prime candidate for using Linq to XML to parse the feed into your application.  Calling the API takes many parameters, but for our purposes we’ll only be using the following parameters:

q
author
v
max-results
start-index

Like any http call these are concatenated onto the uri like any other http address, EG:

If you put this in your browser, then depending on your browser configuration, you may get a Live bookmarks page or a feed subscription.  If you look at the source however you’ll see somthing similar to the following:

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:gml='http://www.opengis.net/gml' xmlns:yt='http://gdata.youtube.com/schemas/2007' xmlns:georss='http://www.georss.org/georss'>
  <id>http://gdata.youtube.com/feeds/api/videos</id>
  <updated>2009-12-30T12:30:41.957Z</updated>
  <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
  <title type='text'>YouTube Videos matching query: lego</title>
  <logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
  <link rel='alternate' type='text/html' href='http://www.youtube.com'/>
  <link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos'/>
  <link rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/batch'/>
  <link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos?q=lego&amp;start-index=1&amp;max-results=25'/>
  <link rel='next' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos?q=lego&amp;start-index=26&amp;max-results=25'/>
  <author>
    <name>YouTube</name>
    <uri>http://www.youtube.com/</uri>
  </author>
  <generator version='2.0' uri='http://gdata.youtube.com/'>YouTube data API</generator>
  <openSearch:totalResults>253375</openSearch:totalResults>
  <openSearch:startIndex>1</openSearch:startIndex>
  <openSearch:itemsPerPage>25</openSearch:itemsPerPage>
  <entry>
    <id>http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU</id>
    <published>2007-06-07T01:16:35.000Z</published>
    <updated>2009-12-30T03:16:50.000Z</updated>
    <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/categories.cat' term='Film' label='Film &amp; Animation'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='Lego'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='Wii'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='Nintendo'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='Mario'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='video'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='game'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='games'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='Luigi'/>
    <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='boobs'/>
    <title type='text'>Lego Wii</title>
    <content type='text'>Thought that was cool? Now checkout www.bidray.com This was my entry for the Nintendo Shortcuts Showcase contest. I litterally finished it days before the deadline and mailed it to them by express mail. I came up with the idea out of no-where. I just woke up early one morning, pulled out all my Lego's and started building. After I was done, I decided to make this film. Tell me what you think! If you all like it...I think I'll do something even more amazing. Oh---btw---I didn't use my ...</content>
    <link rel='alternate' type='text/html' href='http://www.youtube.com/watch?v=F8mZGDFDceU&amp;feature=youtube_gdata'/>
    <link rel='http://gdata.youtube.com/schemas/2007#video.responses' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU/responses'/>
    <link rel='http://gdata.youtube.com/schemas/2007#video.related' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU/related'/>
    <link rel='self' type='application/atom+xml' href='http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU'/>
    <author>
      <name>Kooberz</name>
      <uri>http://gdata.youtube.com/feeds/api/users/kooberz</uri>
    </author>
    <gd:comments>
      <gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU/comments' countHint='13001'/>
    </gd:comments>
    <media:group>
      <media:category label='Film &amp; Animation' scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>Film</media:category>
      <media:content url='http://www.youtube.com/v/F8mZGDFDceU?f=videos&amp;app=youtube_gdata' type='application/x-shockwave-flash' medium='video' isDefault='true' expression='full' duration='281' yt:format='5'/>
      <media:content url='rtsp://v4.cache1.c.youtube.com/CiILENy73wIaGQnlcUMxGJnJFxMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='281' yt:format='1'/>
      <media:content url='rtsp://v7.cache6.c.youtube.com/CiILENy73wIaGQnlcUMxGJnJFxMYESARFEgGUgZ2aWRlb3MM/0/0/0/video.3gp' type='video/3gpp' medium='video' expression='full' duration='281' yt:format='6'/>
      <media:description type='plain'>Thought that was cool? Now checkout www.bidray.com This was my entry for the Nintendo Shortcuts Showcase contest. I litterally finished it days before the deadline and mailed it to them by express mail. I came up with the idea out of no-where. I just woke up early one morning, pulled out all my Lego's and started building. After I was done, I decided to make this film. Tell me what you think! If you all like it...I think I'll do something even more amazing. Oh---btw---I didn't use my ...</media:description>
      <media:keywords>Lego, Wii, Nintendo, Mario, video, game, games, Luigi, boobs</media:keywords>
      <media:player url='http://www.youtube.com/watch?v=F8mZGDFDceU&amp;feature=youtube_gdata'/>
      <media:thumbnail url='http://i.ytimg.com/vi/F8mZGDFDceU/2.jpg' height='90' width='120' time='00:02:20.500'/>
      <media:thumbnail url='http://i.ytimg.com/vi/F8mZGDFDceU/1.jpg' height='90' width='120' time='00:01:10.250'/>
      <media:thumbnail url='http://i.ytimg.com/vi/F8mZGDFDceU/3.jpg' height='90' width='120' time='00:03:30.750'/>
      <media:thumbnail url='http://i.ytimg.com/vi/F8mZGDFDceU/0.jpg' height='240' width='320' time='00:02:20.500'/>
      <media:title type='plain'>Lego Wii</media:title>
      <yt:duration seconds='281'/>
    </media:group>
    <gd:rating average='4.8205233' max='5' min='1' numRaters='19373' rel='http://schemas.google.com/g/2005#overall'/>
    <yt:recorded>2007-06-02</yt:recorded>
    <yt:statistics favoriteCount='22821' viewCount='3393734'/>
  </entry>
  ... more <entry></entry> elements here
</feed>

At this point I’ll mention that there are a few ready made and full fat libraries for You Tube access on Codeplex and there are also now built in methods in .NET 3.5 for dealing with atom and rss based feeds.

So what does each variable do then?

q‘ is the main search parameter, what ever you put in here is what gets searched for in your request, in my example above ‘lego‘ beacuse there are some awesome lego animations on you tube 🙂  This can be left blank, but if you do you need to still append it to your Url eg: use  ‘q=‘ the API documents suggest that you can leave it out, but in reality I found things didn’t quite work as expected if I did.

author‘ is the name of the author you want to filter by. For example if you only wanted to see my videos then you use ‘author=shawtydsyahoo‘ if you use that with a blank q= entry, then you’ll get all the videos posted by that user.  If you fill in the q parameter, then only videos matching the search, and by that author will be returned.

v‘ is the version of the API to use.  In practice I’ve used 1 for all my experiments, but 2 is the current version, you don’t have to add this (I believe that it will default to the most recent) but if you need to set a version for compatibility this is how you do it.

max-results‘ is the maximum number of results you want to return, if this is not specified then the default of 25 or less will be returned, the maximum can be 50.  If there are more results than you have specified a maximum for, then you can use this in leauge with ‘start-index’ to retrieve the results one page at a time, there is also an element returned in the XML that contains a link already pre-made to retrieve the next page.

start-index‘ is the entry number to start at, and defaults to 1 if not provided.  For example, if you where retrieving 10 records at a time, and you wanted the second page, then you would set ‘max-results=10‘ and ‘start-index=11

So how do we parse the results?

This is fairly easy using Linq to XML, but due to the size I’m not going to describe every option returned, also make sure that you use the correct namespaces.  When I first started out with this I was scratching my head for some time trying to figure out why I kept getting empty results from the Linq interpreter, I figured out after a while it was beacuse I wasn’t specifying the required namespaces, and as a result was getting an empty collection.  If you look at the opening feed tag you’ll see that you need the following namespaces to be defined:

XNamespace ns = "http://www.w3.org/2005/Atom";
XNamespace openSearch = "http://a9.com/-/spec/opensearchrss/1.0/";
XNamespace gd = "http://schemas.google.com/g/2005";
XNamespace media = "http://search.yahoo.com/mrss/";
XNamespace yt = "http://gdata.youtube.com/schemas/2007";

then where required use Element( ns + "tagname").Value or Attribute( ns + "attname").Value to get the tag data.

You can load the XML into an XDocument by doing somthing like the following :

string youTubeUrl = "http://gdata.youtube.com/feeds/api/videos?";youTubeUrl += "q=" + searchTerm;
  youTubeUrl += "&author=" + authorFilter;
  youTubeUrl += "&max-results=" + itemsPerPage.ToString();
  youTubeUrl += "&start-index=" + startIndex.ToString();
  youTubeUrl += "&v=1";

XDocument ytDoc = XDocument.Load(youTubeUrl);

Remember as well that the normal rules of accessing URL’s apply here, such as cross domain policy’s, firewalls and other security related stuff.  I’m not going into any of that here as it’s covered in various other places, and in the MSDN docs.

Once we call the above, then after a brief pause ‘ytDoc’ should hold the retrieved feed, ready for parsing.  As I mentioned, I’m not going to go through the entire thing here, but the basics would go somthing like this :

<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns='http://www.w3.org/2005/Atom' xmlns:media='http://search.yahoo.com/mrss/' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gd='http://schemas.google.com/g/2005' xmlns:gml='http://www.opengis.net/gml' xmlns:yt='http://gdata.youtube.com/schemas/2007' xmlns:georss='http://www.georss.org/georss'>
  <id>http://gdata.youtube.com/feeds/api/videos</id>
  <updated>2009-12-30T12:30:41.957Z</updated>
  <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
  <title type='text'>YouTube Videos matching query: lego</title>
  <logo>http://www.youtube.com/img/pic_youtubelogo_123x63.gif</logo>
  <entry>
    <id>http://gdata.youtube.com/feeds/api/videos/F8mZGDFDceU</id>
    <published>2007-06-07T01:16:35.000Z</published>
    <updated>2009-12-30T03:16:50.000Z</updated>
  </entry>
</feed>

I’ve deliberately cut the above short, just so you can get an Idea.

To map the above to an object we first need to build a structure to hold the data.  If you examine the snippet above, you’ll see we have 2 distinct structures.  One for the entry, and one for the feed itself.  To map this we need 2 structures.

for the entry section :

public struct feedEntry
{
   public string id;
   public DateTime published;
   public DateTime updated;
}

and secondly the main feed :

public struct feedResult
{
   public string id;
   public DateTime updated;
   public string title;
   public Uri logo;
   public List<feedEntry>;
}

A few things to notice :

  1. The order I define the classes in.  Even though C# can resolve any order, if you declare your inner objects first, it makes for easier reading.
  2. I’ve left the category tag out, as that along with other missing ones is available elsewhere.
  3. Where I expect multiple inner objects, I’ve specified Lists of the objects declared in advance.

Once we have the required structures declared, then the actual parsing is fairly straight forward :

var data = from xml in ytDoc.Descendants()
           select new feedResult
           {
             id = xml.Element(ns + "id").Value,
             updated = DateTime.Parse(xml.Element(ns + "updated").Value),
             title = xml.Element(ns + "title").Value,
             logo = new Uri(xml.Element(ns + "logo").Value),
             entrys = (from en in xml.Elements(ns + "entry")
                       select new resultEntry
                       {
                         id = en.Element(ns + "id").Value,
                         published = DateTime.Parse(en.Element(ns + "published").Value),
                         updated = DateTime.Parse(en.Element(ns + "updated").Value),
                       }).ToList()
           };

Again to keep the post length down I’ve left a lot of this out 🙂

The good news is however, Iv’e written a full class, that will parse the entire feed which you can down load from my Sky Drive here on my blog.  Please however don’t just nab it and squirrel it away, use it to build a better parser.  Make improvements to it, and send them back to me, learn from it use it as a model to build other XML parsers.  Some of the Linq constructs used are quite complicated, and where possible I’ve tried to handle optional elements gracefully but there is no error handling in it, so if anything is returned that the parser doesn’t expect, then it will in all likely fall over and abort.  If your going to use it in production code, then it will need quite a bit of work to make it bullet proof.  I offer no warranties or anything like that with the code, what you see is what you get, and if I have time to help you with any problems I will, I can’t promise however that I will always be able to.

If you do use it in any of your projects, then all I ask is that you do me the courtesy of acknowledging where it came from and provide a link either back to myself, or to the Lidnug group.

There is far more that could be added to this, including the ability to upload video, and add ratings/comments to existing videos.  As it is however, what we have here provides a good foundation to start from.

To use the class in your projects, add existing item as you would any other class, and change the name-space to suit.  Once you have the source in your project, then simply instantiate a class as follows :

youTubeSearch mySearch = new youTubeSearch("search term", "author", max to return, start index, execute immediately);

so for example :

youTubeSearch mySearch = new youTubeSearch("lego", "", 10, 11, true);

Will search for videos about "lego" without filtering by author, return 10 results per page, starting at the 11th result and executing the search imediately.  If you specify ‘false’ then all that happens is the search is set up ready to be excuted later by calling :

mySearch.executeSearch();

when the search returns, you can find the reply in :

mySearch.results;

with all the objects nested in order underneath that as per the various structures laid out in the source file.  You can use the object explorer, or add a break point in your code to explore the results once you’ve retrieved them, so you can see what is stored where.

That’s all for now, I’m going to go and have a new years drink.

I wish all of you a fantastic new year, and please help to grow Lidnug over the next year, and keep an eye on my blog too, I try to post as often as I can, and you never quite know what going to end up on here 🙂

All the very best for 2010

Shawty

You can download the search class from my Sky Drive at:

You can goto the official Lidnug group here

12 thoughts on “Accessing the You Tube API from C#

  1. Thanks for this; great idea to use Linq to XML for retrieving youtube data. I was playing around with this code a bit, and it would be great to be able to enumerate the categories/subcategories.

    I’ve tried to figure this out, but I can’t seem to do it. Do you have any suggestions?

    Thanks for your time;
    Great code!

    1. Hi Ray,

      Thanks for the comments.

      It’s been a while since I did anything with this, but a quick look at the code gives me the following suggestions:

      For the category tags…

      .element(‘category’).attributes(‘term’).Value

      should get you somewhere near (I’m away from home at the moment so don’t have access to my main PC to look it up, sorry)

      for the media category :

      Film

      Then you’ll need to import the namespace using the XNamespace classes (I think thats the correct name) then when you use the element name, prefix it with the namespace.

      This stackoverflow post will point you in the correct direction…

      http://stackoverflow.com/questions/1453564/how-to-addor-ignore-xml-namespace-when-using-xelement-load

      Hope that helps.

      Shawty

      1. Thanks Shawty for your response; I have tried out what you have suggested, and I think I am close to enumerating the categories (I’m somewhat new to Atom schemas) but I will eventually get it..
        Something that has me completely stumped is how to get a list of music genre’s using your method… For instance, if you navigate to Browser -> Music, there is a list of genre’s such as Alternative, Pop, etc; Do you know if it’s possible to use LINQ to XML to retrieve these genre’s under the Music category?

        Thanks again; very big help indeed.

  2. Hi Ray, have you got a search link that produces the type of XML you want to go through, so I can take a look at the structure.

    Tks

    Shawty

    1. Hey Shawty; it’s strange, but it appears that Youtube hasn’t included the Genre’s of the music category in any atom feed that I can find. For instance, if you go to http://gdata.youtube.com/demo/index.html to access the Youtube api demo, there is an option for the Music categories, but not for any of it’s sub-categories.

      Also, if you have a look at http://code.google.com/p/gdata-issues/issues/detail?id=518 it appears that the youtube api hides subcategories that correspond to music genres… so perhaps this isn’t possible using XML schemas or any other type of data access that I am aware of…

  3. To be honest, that really wouldn’t surprise me where google is concerned.

    It wouldn’t be the first time I’ve come across such things in any services operated by the big G, even today I discover new things that weren’t there but now are, or where there but now are not, and usually with little or no explanation.

    All’s not lost though, remember when you get the XML / JSON or whatever it is your retrieving, just because something that appears in the output is not documented in the API, it doesn’t mean it’s not available.

    Basically if you can see it, then you can parse it just remember that if it’s not official then it may not stay around for long.

    If what your looking for does not appear in the output you requested, then your going to have a hard time getting it, unless you want to resort to do things like page scraping 🙂

  4. I was checking this blog and thanx alot it really helped me. But I am facing a problem getting the auther in the url.
    when i leave the auther empty as shown below, the list box doesn’t make any bindings. is there any way to fix it ?
    Thank you so much.

    private void WebTvButton_Click(object sender, RoutedEventArgs e)
    {
    string youTubeUrl = “http://gdata.youtube.com/feeds/api/videos?”;
    youTubeUrl += “q=” ;
    youTubeUrl += “&author=”;
    youTubeUrl += “&max-results=25” ;
    youTubeUrl += “&start-index=1” ;
    youTubeUrl += “&v=1”;

    WebClient client = new WebClient();
    client.DownloadStringCompleted += HttpsCompleted;
    client.DownloadStringAsync(new Uri(youTubeUrl));
    }

    private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
    if (e.Error == null)
    {
    XNamespace ns = “http://www.w3.org/2005/Atom”;
    XNamespace openSearch = “http://a9.com/-/spec/opensearchrss/1.0/”;
    XNamespace gd = “http://schemas.google.com/g/2005”;
    XNamespace media = “http://search.yahoo.com/mrss/”;
    XNamespace yt = “http://gdata.youtube.com/schemas/2007″;

    XDocument ytDoc = XDocument.Parse(e.Result);

    var data = from xml in ytDoc.Descendants(ns+”entry”)
    select new Entry
    {
    title = xml.Element(ns + “title”).Value,
    published = DateTime.Parse(xml.Element(ns +”published”).Value),
    mediaPlayer = new Uri(xml.Element(media + “group”).Element(media + “player”).Attribute(“url”).Value),
    thumbNails = (from tn in xml.Element(media + “group”).Elements(media + “thumbnail”)
    where tn.Attribute(“url”).Value.Contains(“0.jpg”)
    select new thumbnailEntry
    {
    link = new Uri(tn.Attribute(“url”).Value),
    height = int.Parse(tn.Attribute(“height”).Value),
    width = int.Parse(tn.Attribute(“width”).Value),
    frameTime = TimeSpan.Parse(tn.Attribute(“time”).Value)
    }).ToList(),
    };

    Entry[] DataList = data.ToArray();
    WebTvList.ItemsSource = DataList;
    }
    }

    1. Hi Hasan,

      Glad it helped you so far.

      I’ve tried a URL with a blank author in and the reason it doesn’t return any results is beacuse your asking You Tube to look for videos that have a blank author name. Since an author name is required to post vides, then any search that specifies a blank author name will always return 0 results.

      If you paste the following directly into a browser address bar

      http://gdata.youtube.com/feeds/api/videos?q=lego&author=&max-results=25&start-index=1&v=1

      you’ll get the following XML back

      http://gdata.youtube.com/feeds/api/videos
      2013-09-02T18:12:45.409Z

      Videos matching: lego

      YouTube
      http://www.youtube.com/

      YouTube data API
      0
      1
      25

      Specifically the tag your interested in is this one:

      0

      Which as you can see actually gives you a count of 0 for the results returned.

      The good thing about the parameters used in youtube are most of them are optional, and you don’t have to include the “author” parameter at all if you don’t intend to fill it in with anything.

      if you remove the line

      youTubeUrl += “&author=”;

      completely from your code, it won’t ever attempt to add the parameter to the url to send to youtube, and thus will never attempt to perform an empty search, which will in turn return all entries irrespective of thier author names as you can see if you try the following URL

      http://gdata.youtube.com/feeds/api/videos?q=lego&max-results=25&start-index=1&v=1

      Regards
      Shawty

      1. Let me try putting that XML back in again….

        🙂

        [?xml version=’1.0′ encoding=’UTF-8′?]
        [feed xmlns=’http://www.w3.org/2005/Atom’ xmlns:openSearch=’http://a9.com/-/spec/opensearchrss/1.0/’]
        [id]http://gdata.youtube.com/feeds/api/videos[/id]
        [updated]2013-09-02T18:12:45.409Z[/updated]
        [category scheme=’http://schemas.google.com/g/2005#kind’ term=’http://gdata.youtube.com/schemas/2007#video’/]
        [title type=’text’]Videos matching: lego[/title]
        [logo]http://www.gstatic.com/youtube/img/logo.png[/logo]
        [link rel=’alternate’ type=’text/html’ href=’http://www.youtube.com’/]
        [link rel=’http://schemas.google.com/g/2005#feed’ type=’application/atom+xml’ href=’http://gdata.youtube.com/feeds/api/videos?v=1’/]
        [link rel=’http://schemas.google.com/g/2005#batch’ type=’application/atom+xml’ href=’http://gdata.youtube.com/feeds/api/videos/batch?v=1’/]
        [link rel=’self’ type=’application/atom+xml’ href=’http://gdata.youtube.com/feeds/api/videos?q=lego&author&start-index=1&max-results=25&v=1’/]
        [author]
        [name]YouTube[/name]
        [uri]http://www.youtube.com/[/uri]
        [/author]
        [generator version=’2.1′ uri=’http://gdata.youtube.com’]YouTube data API[/generator]
        [openSearch:totalResults]0[/openSearch:totalResults]
        [openSearch:startIndex]1[/openSearch:startIndex]
        [openSearch:itemsPerPage]25[/openSearch:itemsPerPage]
        [/feed]

        Iv’e replaced the angles with squares, and where is says “Specifically the tag your interested in is this one:” with a 0 underneath it should actually be showing

        [openSearch:totalResults]0[/openSearch:totalResults]

        but with angle brackets for XML.

        Shawty

Leave a comment