Netflix Title Search in C#

I showed in my last post how to do a basic auto complete sort of functionality using Netflix. Today I'm expanding on that a bit and showing some basic search functionality. Using the OAuth class that I created as a base class, we can easily create a set of classes to pull information from Netflix.

    /// <summary>
    /// Helper class to be used with the Netflix API
    /// </summary>
    public class Netflix : OAuth.OAuth
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public Netflix()
            : base()
        {
            this.SignatureType = Signature.HMACSHA1;
        }

        #endregion

        #region Public Functions

        /// <summary>
        /// Calls netflix's auto complete function
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="Term">Your search term</param>
        /// <returns>A list of possible matches</returns>
        public List<string> AutoComplete(string ConsumerKey, string Term)
        {
            List<string> Results = new List<string>();
            string Content = FileManager.GetFileContents(new Uri("http://api.netflix.com/catalog/titles/autocomplete?oauth_consumer_key=" + ConsumerKey + "&term=" + Term));
            XmlDocument Document = new XmlDocument();
            Document.LoadXml(Content);
            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("autocomplete", StringComparison.CurrentCultureIgnoreCase))
                {
                    foreach (XmlNode Child in Children.ChildNodes)
                    {
                        if (Child.Name.Equals("autocomplete_item", StringComparison.CurrentCultureIgnoreCase))
                        {

                            foreach (XmlNode Title in Child.ChildNodes)
                            {
                                if (Title.Name.Equals("title", StringComparison.CurrentCultureIgnoreCase))
                                {
                                    Results.Add(Title.Attributes["short"].Value);
                                }
                            }
                        }
                    }
                }
            }
            return Results; 
        }

        /// <summary>
        /// Title search
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Term">Term to search for</param>
        /// <param name="MaxResults">Max results to return</param>
        /// <param name="StartIndex">The starting index</param>
        /// <returns>List of title information</returns>
        public Titles TitleSearch(string ConsumerKey, string ConsumerKeySecret, string Term, int MaxResults, int StartIndex)
        {
            AddParameter("term", Term);
            AddParameter("max_results", MaxResults.ToString());
            AddParameter("start_index", StartIndex.ToString());
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri("http://api.netflix.com/catalog/titles/");
            return new Titles(FileManager.GetFileContents(new Uri(GenerateRequest())));
        }

        /// <summary>
        /// Title synopsis
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Title">Title to search for</param>
        /// <returns>The synopsis info</returns>
        public string TitleSynopsis(string ConsumerKey, string ConsumerKeySecret, Title Title)
        {
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri(Title.SynopsisLink);
            string Content = FileManager.GetFileContents(new Uri(GenerateRequest()));

            XmlDocument Document = new XmlDocument();
            Document.LoadXml(Content);
            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("synopsis", StringComparison.CurrentCultureIgnoreCase))
                {
                    return Children.InnerText;
                }
            }

            return "";
        }

        /// <summary>
        /// Cast search
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Title">Title to search for</param>
        /// <returns>List of people information</returns>
        public People CastLookup(string ConsumerKey, string ConsumerKeySecret, Title Title)
        {
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri(Title.CastLink);
            return new People(FileManager.GetFileContents(new Uri(GenerateRequest())));
        }

        /// <summary>
        /// Director search
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Title">Title to search for</param>
        /// <returns>List of people information</returns>
        public People DirectorLookup(string ConsumerKey, string ConsumerKeySecret, Title Title)
        {
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri(Title.DirectorsLink);
            return new People(FileManager.GetFileContents(new Uri(GenerateRequest())));
        }

        /// <summary>
        /// Similar title search
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Title">Title to use as our search base</param>
        /// <returns>List of title information</returns>
        public Titles SimilarTitles(string ConsumerKey, string ConsumerKeySecret, Title Title)
        {
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri(Title.SimilarTitleLink);
            return new Titles(FileManager.GetFileContents(new Uri(GenerateRequest())));
        }

        /// <summary>
        /// Formats available for the title
        /// </summary>
        /// <param name="ConsumerKey">Consumer key</param>
        /// <param name="ConsumerKeySecret">Consumer key secret</param>
        /// <param name="Title">Title to use as our search base</param>
        /// <returns>List of available formats</returns>
        public List<string> FormatsAvailable(string ConsumerKey, string ConsumerKeySecret, Title Title)
        {
            this.ConsumerKey = ConsumerKey;
            this.ConsumerKeySecret = ConsumerKeySecret;
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri(Title.FormatsAvailableLink);
            string Content = FileManager.GetFileContents(new Uri(GenerateRequest()));

            List<string> Results = new List<string>();
            XmlDocument Document = new XmlDocument();
            Document.LoadXml(Content);
            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("delivery_formats", StringComparison.CurrentCultureIgnoreCase))
                {
                    foreach (XmlNode Child in Children.ChildNodes)
                    {
                        if (Child.Name.Equals("availability", StringComparison.CurrentCultureIgnoreCase))
                        {
                            foreach (XmlNode Category in Child.ChildNodes)
                            {
                                if (Category.Name.Equals("category", StringComparison.CurrentCultureIgnoreCase))
                                {
                                    Results.Add(Category.Attributes["term"].Value);
                                }
                            }
                        }
                    }
                }
            }
            return Results;
        }

        #endregion
    }

The code above has a number of functions that can be used to pull information. The main function that you'd be interested in is TitleSearch. It takes your consumer key, secret, search term, starting index, etc. The consumer key and secret key can be retrieved by signing up at netflix's developer center. But with that function, a helper class (Titles) is used to parse the information and return it so that it can be used. That along with the other helpers can be found below:

    /// <summary>
    /// List of titles
    /// </summary>
    public class Titles
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="XMLContent">XML Content</param>
        public Titles(string XMLContent)
        {
            XmlDocument Document = new XmlDocument();
            Document.LoadXml(XMLContent);
            TitleList = new List<Title>();

            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("catalog_titles", StringComparison.CurrentCultureIgnoreCase)
                    || Children.Name.Equals("similars", StringComparison.CurrentCultureIgnoreCase))
                {
                    foreach (XmlNode Child in Children.ChildNodes)
                    {
                        if (Child.Name.Equals("catalog_title", StringComparison.CurrentCultureIgnoreCase)
                            || Child.Name.Equals("similars_item", StringComparison.CurrentCultureIgnoreCase))
                        {
                            TitleList.Add(new Title((XmlElement)Child));
                        }
                        else if (Child.Name.Equals("number_of_results", StringComparison.CurrentCultureIgnoreCase))
                        {
                            NumberOfResults = int.Parse(Child.InnerText);
                        }
                        else if (Child.Name.Equals("start_index", StringComparison.CurrentCultureIgnoreCase))
                        {
                            StartIndex = int.Parse(Child.InnerText);
                        }
                        else if (Child.Name.Equals("results_per_page", StringComparison.CurrentCultureIgnoreCase))
                        {
                            ResultsPerPage = int.Parse(Child.InnerText);
                        }
                    }
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Number of results
        /// </summary>
        public int NumberOfResults { get; set; }

        /// <summary>
        /// Start index
        /// </summary>
        public int StartIndex { get; set; }

        /// <summary>
        /// Results per page
        /// </summary>
        public int ResultsPerPage { get; set; }

        /// <summary>
        /// List of titles
        /// </summary>
        public List<Title> TitleList { get; set; }

        #endregion
    }

 

    /// <summary>
    /// Title information
    /// </summary>
    public class Title
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Element">Title information</param>
        public Title(XmlElement Element)
        {
            Categories = new List<string>();
            foreach (XmlNode Children in Element.ChildNodes)
            {
                if (Children.Name.Equals("id", StringComparison.CurrentCultureIgnoreCase))
                {
                    ID = Children.InnerText;
                }
                else if (Children.Name.Equals("title", StringComparison.CurrentCultureIgnoreCase))
                {
                    TitleInformation = new TitleInformation((XmlElement)Children);
                }
                else if (Children.Name.Equals("box_art", StringComparison.CurrentCultureIgnoreCase))
                {
                    BoxArt = new BoxArt((XmlElement)Children);
                }
                else if (Children.Name.Equals("release_year", StringComparison.CurrentCultureIgnoreCase))
                {
                    ReleaseYear = Children.InnerText;
                }
                else if (Children.Name.Equals("runtime", StringComparison.CurrentCultureIgnoreCase))
                {
                    RunTime = int.Parse(Children.InnerText);
                }
                else if (Children.Name.Equals("average_rating", StringComparison.CurrentCultureIgnoreCase))
                {
                    AverageRating = double.Parse(Children.InnerText);
                }

                else if (Children.Name.Equals("category", StringComparison.CurrentCultureIgnoreCase))
                {
                    if (Children.Attributes["scheme"] != null
                        && (Children.Attributes["scheme"].Value.Equals("http://api.netflix.com/categories/mpaa_ratings",
                            StringComparison.CurrentCultureIgnoreCase)
                            || Children.Attributes["scheme"].Value.Equals("http://api.netflix.com/categories/tv_ratingss",
                            StringComparison.CurrentCultureIgnoreCase)))
                    {
                        Rating = Children.Attributes["label"].Value;
                    }
                    else if (Children.Attributes["scheme"].Value.Equals("http://api.netflix.com/categories/genres",
                        StringComparison.CurrentCultureIgnoreCase))
                    {
                        Categories.Add(Children.Attributes["label"].Value);
                    }
                }
                else if (Children.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase))
                {
                    if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles/synopsis",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        SynopsisLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/people.cast",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        CastLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/people.directors",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        DirectorsLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles/format_availability",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        FormatsAvailableLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles/screen_formats",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        ScreenFormatsLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles/languages_and_audio",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        LanguagesLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles.similars",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        SimilarTitleLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("alternate",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                         this.NetflixWebPage= Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles.series",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        this.SeriesLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/programs",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        this.EpisodeLink = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catalog/titles/official_url",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        this.StudioWebPage = Children.Attributes["href"].Value;
                    }
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Title ID
        /// </summary>
        public string ID { get; set; }

        /// <summary>
        /// Title information
        /// </summary>
        public TitleInformation TitleInformation { get; set; }

        /// <summary>
        /// Box artwork
        /// </summary>
        public BoxArt BoxArt { get; set; }

        /// <summary>
        /// Synopsis link
        /// </summary>
        public string SynopsisLink { get; set; }

        /// <summary>
        /// Year released
        /// </summary>
        public string ReleaseYear { get; set; }

        /// <summary>
        /// Average rating
        /// </summary>
        public string Rating { get; set; }

        /// <summary>
        /// list of categories
        /// </summary>
        public List<string> Categories { get; set; }

        /// <summary>
        /// Run time in seconds
        /// </summary>
        public int RunTime { get; set; }

        /// <summary>
        /// Link to cast information
        /// </summary>
        public string CastLink { get; set; }

        /// <summary>
        /// Link to directories information
        /// </summary>
        public string DirectorsLink { get; set; }

        /// <summary>
        /// Formats available link
        /// </summary>
        public string FormatsAvailableLink { get; set; }

        /// <summary>
        /// Screen formats available link
        /// </summary>
        public string ScreenFormatsLink { get; set; }

        /// <summary>
        /// Language information link
        /// </summary>
        public string LanguagesLink { get; set; }

        /// <summary>
        /// Average rating
        /// </summary>
        public double AverageRating { get; set; }

        /// <summary>
        /// Similar titles to this one link
        /// </summary>
        public string SimilarTitleLink { get; set; }

        /// <summary>
        /// Link to netflix webpage
        /// </summary>
        public string NetflixWebPage { get; set; }

        /// <summary>
        /// Link to studio web page
        /// </summary>
        public string StudioWebPage { get; set; }

        /// <summary>
        /// Link to awards
        /// </summary>
        public string Awards { get; set; }

        /// <summary>
        /// Link to bonus material
        /// </summary>
        public string BonusMaterial { get; set; }

        /// <summary>
        /// Link for series info
        /// </summary>
        public string SeriesLink { get; set; }

        /// <summary>
        /// Link for episodes
        /// </summary>
        public string EpisodeLink { get; set; }

        /// <summary>
        /// Season info link
        /// </summary>
        public string SeasonLink { get; set; }

        #endregion
    }

 

    /// <summary>
    /// Title information
    /// </summary>
    public class TitleInformation
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Element">Element information</param>
        public TitleInformation(XmlElement Element)
        {
            if (Element.Attributes["short"] != null)
            {
                ShortName = Element.Attributes["short"].Value;
            }
            if (Element.Attributes["regular"] != null)
            {
                RegularName = Element.Attributes["regular"].Value;
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Short name
        /// </summary>
        public string ShortName { get; set; }

        /// <summary>
        /// Regular name
        /// </summary>
        public string RegularName { get; set; }

        #endregion
    }

 

    /// <summary>
    /// Box art information
    /// </summary>
    public class BoxArt
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Element">Element information</param>
        public BoxArt(XmlElement Element)
        {
            if (Element.Attributes["small"] != null)
            {
                SmallPicture = Element.Attributes["small"].Value;
            }
            if (Element.Attributes["medium"] != null)
            {
                MediumPicture = Element.Attributes["medium"].Value;
            }
            if (Element.Attributes["large"] != null)
            {
                LargePicture = Element.Attributes["large"].Value;
            }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Small picture
        /// </summary>
        public string SmallPicture { get; set; }

        /// <summary>
        /// Medium picture
        /// </summary>
        public string MediumPicture { get; set; }

        /// <summary>
        /// Large picture
        /// </summary>
        public string LargePicture { get; set; }

        #endregion
    }

 

    /// <summary>
    /// People list
    /// </summary>
    public class People
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="XMLContent">XML content</param>
        public People(string XMLContent)
        {
            XmlDocument Document = new XmlDocument();
            Document.LoadXml(XMLContent);
            PeopleList = new List<Person>();

            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("people", StringComparison.CurrentCultureIgnoreCase))
                {
                    foreach (XmlNode Child in Children.ChildNodes)
                    {
                        if (Child.Name.Equals("person", StringComparison.CurrentCultureIgnoreCase))
                        {
                            PeopleList.Add(new Person((XmlElement)Child));
                        }
                        else if (Child.Name.Equals("number_of_results", StringComparison.CurrentCultureIgnoreCase))
                        {
                            NumberOfResults = int.Parse(Child.InnerText);
                        }
                        else if (Child.Name.Equals("start_index", StringComparison.CurrentCultureIgnoreCase))
                        {
                            StartIndex = int.Parse(Child.InnerText);
                        }
                        else if (Child.Name.Equals("results_per_page", StringComparison.CurrentCultureIgnoreCase))
                        {
                            ResultsPerPage = int.Parse(Child.InnerText);
                        }
                    }
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// List of people
        /// </summary>
        public List<Person> PeopleList { get; set; }

        /// <summary>
        /// Number of results
        /// </summary>
        public int NumberOfResults { get; set; }

        /// <summary>
        /// Start index
        /// </summary>
        public int StartIndex { get; set; }

        /// <summary>
        /// Number of results per page
        /// </summary>
        public int ResultsPerPage { get; set; }

        #endregion
    }

 

    /// <summary>
    /// Person class
    /// </summary>
    public class Person
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Element">person node</param>
        public Person(XmlElement Element)
        {
            foreach (XmlNode Children in Element.ChildNodes)
            {
                if (Children.Name.Equals("id", StringComparison.CurrentCultureIgnoreCase))
                {
                    ID = Children.InnerText;
                }
                else if (Children.Name.Equals("name", StringComparison.CurrentCultureIgnoreCase))
                {
                    Name = Children.InnerText;
                }
                else if (Children.Name.Equals("bio", StringComparison.CurrentCultureIgnoreCase))
                {
                    Bio = Children.InnerText;
                }
                else if (Children.Name.Equals("link", StringComparison.CurrentCultureIgnoreCase))
                {
                    if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("http://schemas.netflix.com/catlog/person/filmography",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        Filmography = Children.Attributes["href"].Value;
                    }
                    else if (Children.Attributes["rel"] != null
                        && Children.Attributes["rel"].Value.Equals("alternate",
                            StringComparison.CurrentCultureIgnoreCase))
                    {
                        Webpage = Children.Attributes["href"].Value;
                    }
                }
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// ID for the person
        /// </summary>
        public string ID { get; set; }

        /// <summary>
        /// Name of the person
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// Bio information for the person
        /// </summary>
        public string Bio { get; set; }

        /// <summary>
        /// Filmography link
        /// </summary>
        public string Filmography { get; set; }

        /// <summary>
        /// Webpage link
        /// </summary>
        public string Webpage { get; set; }

        #endregion
    }

 

I realize that's a lot of code, but all it does is takes the XML that is returned and parses it out so that we can use the information. Most of the items are going to be rather obvious with one exception. Netflix doesn't return information directly in a normal search (well they can but it requires an extra couple of parameters). Instead what they do is they return a link that can be used for another query that will return that information. The reason for this is to save space. For instance, you may do a query and have 1000+ items returned. To save space, it's easier if you return the link instead of the whole description for a movie (or the cast, with all of their information, etc.).

So when you call TitleSearch, you're getting the basic information for the items and links to everything else that you would want. Which is why there are functions for things link pulling cast information, formats that it's available in, etc. As I said, we can actually have Netflix expand that information for us but once again I'd just go with the multiple queries unless it's information you know that you will need. Anyway, hopefully the code is of use to someone out there. So try it out, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 9/24/2009 at 9:27 AM
Tags: , ,
Categories: C#
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Auto Complete Search of the Netflix API in C#

In my last post I talked about OAuth and the fact that I was using it when dealing with the Netflix API. Well it turns out that not everything in the API requires OAuth. There is one portion of the API that requires very little (with the exception of a consumer key which you can get from the Netflix developer section). Specifically the auto complete functionality of the API. In order to do their auto complete search all you need is the consumer key and a search term. It gets fed to the system like OAuth does (a simple URL with the appropriate query string), but we don't have to deal with a signature, etc. So lets look at the code to get this done:

        public List<string> AutoComplete(string ConsumerKey, string Term)
        {
            List<string> Results = new List<string>();
            string Content = FileManager.GetFileContents(new Uri("http://api.netflix.com/catalog/titles/autocomplete?oauth_consumer_key=" + ConsumerKey + "&term=" + Term));
            XmlDocument Document = new XmlDocument();
            Document.LoadXml(Content);
            foreach (XmlNode Children in Document.ChildNodes)
            {
                if (Children.Name.Equals("autocomplete", StringComparison.CurrentCultureIgnoreCase))
                {
                    foreach (XmlNode Child in Children.ChildNodes)
                    {
                        if (Child.Name.Equals("autocomplete_item", StringComparison.CurrentCultureIgnoreCase))
                        {

                            foreach (XmlNode Title in Child.ChildNodes)
                            {
                                if (Title.Name.Equals("title", StringComparison.CurrentCultureIgnoreCase))
                                {
                                    Results.Add(Title.Attributes["short"].Value);
                                }
                            }
                        }
                    }
                }
            }
            return Results;
        }

The code above does the call to the API using the FileManager class from my utility library. Once the response is read, it loads the XML that is returned and parses it, looking for the title content. I then put those values into a list of strings and return it. That's all there is to it really. So give it a try, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 9/22/2009 at 9:34 AM
Tags: , ,
Categories: C#
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

OAuth Helper in C#

The app that I've been working on in my spare time (well one of them anyway), interfaces with netflix. Netflix, for those who are unaware, has a developer API that you can use in various applications. The main issue that you'll run into is that the documentation that they give is, well, it could be better. Especially if you've never used OAuth before as it is used along with REST in their API.

OAuth is a protocol that is used to help in authorization. When it comes down to it, it's just an URL with the information added as a query string. This makes it extremely easy to use, especially if you have some experience with it. I am unfortunately one of those individuals that had never used it before, but thankfully the specs are pretty straightforward enough. Anyway, after taking a look at some other people's code, I came up with a base class to help me if I ever need to do anything with OAuth again:

    /// <summary>
    /// OAuth base class
    /// </summary>
    public class OAuth
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public OAuth()
        {
            Parameters = new System.Collections.Generic.List<Pair<string,string>>();
            AddParameter("oauth_consumer_key", "");
            AddParameter("oauth_nonce", "");
            AddParameter("oauth_signature_method", "");
            AddParameter("oauth_timestamp", "");
            AddParameter("oauth_version", "1.0");
            RandomGenerator = new Random();
        }

        #endregion

        #region Protected Functions

        /// <summary>
        /// Generates a request
        /// </summary>
        /// <returns>The string containing the request</returns>
        protected string GenerateRequest()
        {
            string Url="";
            string Parameters="";
            string Signature=GenerateSignature(out Url,out Parameters);
            return Url.ToString() + "?" + Parameters + "&oauth_signature=" + UrlEncode(Signature);
        }

        /// <summary>
        /// Generates the signature
        /// </summary>
        /// <param name="Url">Url</param>
        /// <param name="Parameters">Parameters</param>
        /// <returns>The signature</returns>
        protected string GenerateSignature(out string Url,out string Parameters)
        {
            Parameters = "";
            Url = "";

            if (this.SignatureType == Signature.HMACSHA1)
            {
                string Base = GenerateBase(out Url, out Parameters);
                HMACSHA1 SHA1 = new HMACSHA1();
                SHA1.Key = Encoding.ASCII.GetBytes(UrlEncode(ConsumerKeySecret) + "&" + (string.IsNullOrEmpty(TokenSecret) ? "" : UrlEncode(TokenSecret)));
                return Convert.ToBase64String(SHA1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(Base)));
            }
            else if (this.SignatureType == Signature.RSASHA1)
            {
                throw new NotImplementedException();
            }
            else if (this.SignatureType == Signature.PLAINTEXT)
            {
                return UrlEncode(ConsumerKeySecret + "&" + TokenSecret);
            }
            return "";
        }

        /// <summary>
        /// Does url encoding using uppercase since that is needed for .Net
        /// </summary>
        /// <param name="Input">Input string</param>
        /// <returns>Url encoded string</returns>
        protected string UrlEncode(string Input)
        {
            StringBuilder Result = new StringBuilder();
            for (int x = 0; x < Input.Length; ++x)
            {
                if (UnreservedChars.IndexOf(Input[x]) != -1)
                    Result.Append(Input[x]);
                else
                    Result.Append("%" + String.Format("{0:X2}", (int)Input[x]));
            }
            return Result.ToString();
        }

        /// <summary>
        /// Adds a parameter
        /// </summary>
        /// <param name="Key">Key text</param>
        /// <param name="Value">Value text</param>
        protected void AddParameter(string Key, string Value)
        {
            bool Found = false;
            foreach (Pair<string, string> Pair in Parameters)
            {
                if (Pair.Left == Key)
                {
                    Pair.Right = Value;
                    Found = true;
                    break;
                }
            }
            if (!Found)
            {
                Parameters.Add(new Pair<string, string>(Key, Value));
            }
        }

        #endregion

        #region Private Functions

        /// <summary>
        /// Generates the info used in the signature
        /// </summary>
        /// <param name="UrlString">Url string</param>
        /// <param name="ParameterString">Parameter string</param>
        /// <returns>The base information for the signature</returns>
        private string GenerateBase(out string UrlString, out string ParameterString)
        {
            StringBuilder UrlBuilder = new StringBuilder();
            StringBuilder Builder = new StringBuilder();
            StringBuilder ParameterBuilder = new StringBuilder();

            string SignatureMethod = "";
            if (this.SignatureType == Signature.HMACSHA1)
                SignatureMethod = "HMAC-SHA1";
            else if (this.SignatureType == Signature.RSASHA1)
                SignatureMethod = "RSA-SHA1";
            else if (this.SignatureType == Signature.PLAINTEXT)
                SignatureMethod = "PLAINTEXT";

            AddParameter("oauth_consumer_key", this.ConsumerKey);
            AddParameter("oauth_nonce", RandomGenerator.Next(123400, 9999999).ToString());
            AddParameter("oauth_signature_method", SignatureMethod);
            AddParameter("oauth_timestamp", Convert.ToInt64((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds).ToString());
            AddParameter("oauth_version", "1.0");

            if (!string.IsNullOrEmpty(this.Token))
                AddParameter("oauth_token", this.Token);

            Parameters.Sort(new PairComparer());

            string Splitter = "";
            foreach (Pair<string, string> Key in Parameters)
            {
                ParameterBuilder.Append(Splitter)
                    .Append(Key.Left)
                    .Append("=")
                    .Append(UrlEncode(Key.Right));
                Splitter = "&";
            }

            UrlBuilder.Append(Url.Scheme).Append("://").Append(Url.Host);
            if ((Url.Scheme == "http" && Url.Port != 80) || (Url.Scheme == "https" && Url.Port != 443))
                UrlBuilder.Append(":").Append(Url.Port);
            UrlBuilder.Append(Url.AbsolutePath);

            UrlString = UrlBuilder.ToString();
            ParameterString = ParameterBuilder.ToString();

            Builder.Append(this.Method.ToString().ToUpper())
                .Append("&")
                .Append(UrlEncode(UrlBuilder.ToString()))
                .Append("&")
                .Append(UrlEncode(ParameterBuilder.ToString()));

            return Builder.ToString();
        }

        #endregion

        #region Protected Properties

        /// <summary>
        /// Url that is being used
        /// </summary>
        protected Uri Url { get; set; }

        /// <summary>
        /// Consumer key
        /// </summary>
        protected string ConsumerKey { get; set; }

        /// <summary>
        /// Consumer key secret
        /// </summary>
        protected string ConsumerKeySecret { get; set; }

        /// <summary>
        /// Token
        /// </summary>
        protected string Token { get; set; }

        /// <summary>
        /// Token secret
        /// </summary>
        protected string TokenSecret { get; set; }
       
        /// <summary>
        /// HTTP Method
        /// </summary>
        protected HTTPMethod Method { get; set; }

        /// <summary>
        /// The hash type that is suppose to be used
        /// </summary>
        protected Signature SignatureType { get; set; }

        #endregion

        #region Private Variables

        private string UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
        private System.Collections.Generic.List<Pair<string, string>> Parameters { get; set; }
        private Random RandomGenerator { get; set; }

        #endregion

        #region Private Classes

        /// <summary>
        /// Comparer class for the pair type
        /// </summary>
        private class PairComparer : IComparer<Pair<string,string>>
        {
            public int Compare(Pair<string, string> x, Pair<string, string> y)
            {
                if (x.Left == y.Left)
                {
                    return string.Compare(x.Right, y.Right);
                }
                else
                {
                    return string.Compare(x.Left, y.Left);
                }
            }
        }

        #endregion
    }

    #region Enums

    /// <summary>
    /// HTTP Method
    /// </summary>
    public enum HTTPMethod
    {
        GET,
        POST,
        DELETE,
        PUT
    }

    /// <summary>
    /// Hash type
    /// </summary>
    public enum Signature
    {
        PLAINTEXT,
        RSASHA1,
        HMACSHA1
    }

    #endregion

The pair class that it uses can be found here:

/// <summary>
    /// Pairs two items together
    /// </summary>
    /// <typeparam name="T1">Type for the left hand side</typeparam>
    /// <typeparam name="T2">Type for the right hand side</typeparam>
    public class Pair<T1, T2>
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public Pair()
        {

        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Left">Left hand side of the pair</param>
        /// <param name="Right">Right hand side of the pair</param>
        public Pair(T1 Left, T2 Right)
        {
            this.Left = Left;
            this.Right = Right;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Left hand item
        /// </summary>
        public T1 Left { get; set; }

        /// <summary>
        /// Right hand item
        /// </summary>
        public T2 Right { get; set; }

        #endregion

        #region Public Overridden Properties

        public override int GetHashCode()
        {
            if (Left != null && Right != null)
            {
                return Left.GetHashCode() ^ Right.GetHashCode();
            }
            return 0;
        }

        public override bool Equals(object obj)
        {
            if (obj != null && obj is Pair<T1, T2>)
            {
                return Equals(Left, ((Pair<T1, T2>)obj).Left) && Equals(Right, ((Pair<T1, T2>)obj).Right);
            }
            return false;
        }

        #endregion
    }

The pair code should be rather straight forward, the OAuth code on the other hand may need some explaining.There is really only one function that you should need to call, GenerateRequest. However prior to calling that, you will need to set up the object (adding the parameters that it needs). Specifically you need to add the url, signature type (hashing type), HTTP method (GET, POST, DELETE, PUT), as well as the consumer key and consumer key secret. The consumer key and consumer key secret are supplied by the service provider (in my case Netflix). The signature type (the only other parameter that you may have questions with), is really the hashing algorithm that should be used. OAuth uses three different options when hashing, HMAC-SHA1, RSA-SHA1, and plain text. HMAC-SHA1 is what netflix uses and thus I put in the code for that along with plain text for testing purposes. RSA-SHA1 isn't implemented but it should be fairly easy to add

There are a couple quirks though when dealing with OAuth. First, the query string needs to be sorted by the key. On top of that, since it is simply an url that you will be getting back, it needs to use url encoding. However you will notice that I'm not using the built in function in .Net. The reason for this is the simple fact that the .Net implementation uses lower case and OAuth seems to require upper case. That's it though (and the code above takes care of all of that). All you need to do is inherit from the base class here and set things up. So try it out, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 9/18/2009 at 11:09 AM
Tags: , , ,
Categories: C#
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed