Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

Using YouTube API 1.9.0.0 with MVVM-Light-Toolkit

Rate me:
Please Sign up or sign in to vote.
4.58/5 (8 votes)
28 Nov 2016CPOL5 min read 45.3K   1.2K   21   5
Receive YouTube Feeds with API 1.9.0.0 using MVVM Light Toolkit

330191/Unbenannt-1.jpg

Introduction

First article for CodeProject, hope it will be useful for this great community! It's quite a simple YouTubeViewer which implements the YouTube API, based on the MVVM Architecture. I'm using the MVVM Light Toolkit, so this article should support you to understand how MVVM and the YouTube API works.

What is MVVM and why should I use it?

The goal of MVVM is to seperate your UI from Code. So when you're finished and there's absolutly no code behind in your main window, you're doing the right way. The advantages of this architecture would fill many other articles, in summary we can say that a needed redesign of the GUI could easily be done by UX-Designers without studying the code logic.

We're working with bindings only!

In WPF there are several ways to store data. For MVVM you should use ObservableCollection<> because as the name implies, the collection observes changes/updates and automatically notifies the UI.

Binding/Commands

WPF Controls are able to bind a DataContext, in this example we bind a ViewModel to the Main Window. Controls like ListView's, Listbox, Combobox delievers the "ItemsSource" attribute to bind the ObservableCollection to the Control. The RaisePropertyChanged is used to notify the UI that data has changed. RelayCommand and the implemented ICommand are used to bind commands to the UI Controls, many controls supports this.

MVVM Rules/Advanteges

More advantages of the MVVM-Architecture @ following link:

http://ahaageeks.blogspot.com/2010/03/mvvm-model-view-viewmodel.html

“No code in the code behind.”

“If you have code in your View’s code behind, MVVM Police is going to take your family away.”

There is something called “Code Behind For a Reason”. But what I see the problems is that it is not that easy to test your Views if there are code in your code behind.

“No need for Converters.”

Sometimes you might need to use Converters, but I would say it is LESS use in MVVM.

“MVVM is only suitable for BIG projects.”

It is suitable in lot of cases that we can gain advantages of Data Binding.

“MVVM Costs Performance.”

Well, I would say Developer Performance since it requires some skills to understand the Patter and you have to do some additional coding such as Commands etc. But I think the Data Bindings are very optimized. You mileage may vary.

“MVVM is complex. Makes my head hurt”

Well, its nothing but a Pattern. The rest of the stuff are helpers and if your head hurts, it probably becoz of Last Night :)

“I dont need separation coz I dont have a designer.”

I think separation is about maintainability, and testability other than blend-ability.

“You cannot use MVVM with other patterns.”

Well, Why Not. You can use in a User Control with MVVM, and use this User Control in any other places.

Using the code

Requirements

YouTube SDK 1.9.0.0 from http://code.google.com/p/google-gdata/downloads/list

MVVM Light Toolkit from http://mvvmlight.codeplex.com/

Adobe Flash Player installed

Microsoft Expression Blend SDK

Namespaces:

C#
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using Google.GData.Client;
using Google.YouTube;

Working with the YouTube API

We need to authenticate our Application first. Please create a developer key within your youtube account.

QueryClientLoginToken returns a string if user's credentials are valid, otherwise it will throw an exception. For the case that the User is trying to log in with wrong credentials several times, we're catching the CaptchaRequiredException also.

YouTubeService service = new YouTubeService("APP_NAME", "DEVELOPER_KEY");
service.setUserCredentials("USERNAME","PASSWORD");
            
try
{
     service.QueryClientLoginToken();    
}
catch (Exception ex)
{
        if (ex is Google.GData.Client.CaptchaRequiredException || ex is Google.GData.Client.InvalidCredentialsException)
        {           
             Console.WriteLine(ex.Message.ToString());     
        }
        else
        {
             Console.WriteLine(ex.Message.ToString());
        }
}

Now we're able to call a new request to get YouTube Video Feeds, Playlists, ...

try
{       
YouTubeRequestSettings settings = new  YouTubeRequestSettings("APP_NAME", "DEVELOPER_KEY", "YouTube_USERNAME", "YouTube_PASSWORD");
      if (settings != null)
      reqeuest = new YouTubeRequest(settings);
}
catch (Exception ex)
{
      if (ex is Google.GData.Client.CaptchaRequiredException || ex is Google.GData.Client.InvalidCredentialsException || ex is GDataRequestException)
      {
          Console.WriteLine(ex.Message.ToString());
      }
      else
      {
          Console.WriteLine(ex.Message.ToString());
      }
}

To store general VideoFeed data we need our ObservableCollection, for this sample I've created a second Collection to load VideoComments after a video is selected in the ListView (For an improved performance, comments will be loaded when item x gets selected)

C#
private ObservableCollection<you_tube_data> _detail_list = new ObservableCollection<you_tube_data>();
private ObservableCollection<string> _video_comments = new ObservableCollection<string>();

Additional class to store the video details.

C#
public class you_tube_data
{
            public string video_title { get; set; }
            public string video_thumb_url { get; set; }
            public string video_summary { get; set; }
            public string video_id { get; set; }
            public string video_uploader { get; set; }
            public string video_view_count { get; set; }
            public string video_rating { get; set; }
            public string video_description { get; set; }
}

Let's search for videos and store video details in the ObservableCollection.

C#
try
{
         string uriSearchTerm = System.Web.HttpUtility.UrlEncode(SearchTerm);
         detail_list.Clear();
         Request();
         //Get video feed for your search term
         Feed<Video> vid_feed = reqeuest.Get<Video>(new Uri("https://gdata.youtube.com/feeds/api/videos?q=" + uriSearchTerm + "&key=DEVELOPER_KEY"));
         //save video details to observable collection
         foreach (Video entry in vid_feed.Entries)
         {
              detail_list.Add(new you_tube_data {video_description=entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });
         }
}
catch (Exception ex)
{
         Console.WriteLine(ex.Message.ToString());
}

Navigate to the next/previous 25 Entries.

To show the next 25 feeds, we take the URL and our seachterm and we're going to add 25 to &start-index=INDEX. Per default YouTube returns 25 feeds.

C#
try
{
       string uriSearchTerm = System.Web.HttpUtility.UrlEncode(SearchTerm);
       detail_list.Clear();
       Request();
       //Get video feed for your search term
       index+=25;
       Feed<Video> vid_feed = reqeuest.Get<Video>(new Uri("https://gdata.youtube.com/feeds/api/videos?q=" + uriSearchTerm + "&key=DEVELOPER_KEY" + "&start-index=" + index.ToString()));
       //save video details to observable collection 
       foreach (Video entry in vid_feed.Entries)
       {
           detail_list.Add(new you_tube_data {video_description=entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });
       }
}
catch (Exception ex)
{
      Console.WriteLine(ex.Message.ToString());
}

Customize the WebBrowser Control

A little challange here is that we have to face with the WebBrowser Control which is based on an old Win32 Component, so binding the URL to the Control is not possible per default.

Let's create a customized WebBrowser UserControl.

The XAML looks usual:

C#
<Grid>
<WebBrowser x:Name="browser" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" />
</Grid>

Code Behind:

Now we can use our customized WebBrowser UserControl to bind the YouTube Video-URL to the Control.

As following:

  1. Create a new DependencyProperty
  2. If property changed (in our case when the user selects a video), Browse() gets called.
  3. Display(string url) is called an we notify the default YouTube-URL + our VideoID.
  4. Create a new simple HTML-Page which contains the Video as Shockwave Flash Player.
C#
private static readonly DependencyProperty HtmlTextProperty = DependencyProperty.Register("HtmlText", typeof(string), typeof(CustomWebbrowserControl));

public string HtmlText
        {
            get { return (string)GetValue(HtmlTextProperty); }
            set { SetValue(HtmlTextProperty, value); }
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);
            if (e.Property == HtmlTextProperty)
            {
                Browse();
            }
        }

        private void Browse()
        {
            if (!string.IsNullOrEmpty(HtmlText))
            {
                Display("http://www.youtube.com/watch?v=" + HtmlText);
            }
        }

        private void Display(string url)
        {
            Match m = YouTubeURLIDRegex.Match(url);
            String id = m.Groups["v"].Value;
            string page =
            "<!DOCTYPE html PUBLIC \" -//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\" >\r\n"
            + @"<!-- saved from url=(0022)http://www.youtube.com -->" + "\r\n"
            + "<html><body scroll=\"no\" leftmargin=\"0px\" topmargin=\"0px\" marginwidth=\"0px\" marginheight=\"0px\" >" + "\r\n"
            + GetYouTubeScript_playlist(id)
            + "</body></html>\r\n";

            browser.NavigateToString(page);
          
        }

        private string GetYouTubeScript_playlist(string id)
        {
            string scr = "";
            scr = @"<object width='400' height='300'> " + "\r\n";
            scr = scr + @"<param name='movie' value='http://www.youtube.com/v/" + HtmlText + "?version=3" + "'></param> " + "\r\n";
            scr = scr + @"<param name='allowFullScreen' value='false'></param> " + "\r\n";
            scr = scr + @"<param name='allowscriptaccess' value='always'></param> " + "\r\n";
            scr = scr + @"<embed src='http://www.youtube.com/v/" + HtmlText + "?version=3" + "' ";
            scr = scr + @"type='application/x-shockwave-flash' allowscriptaccess='always' ";
            scr = scr + @"allowfullscreen='false' width='400' height='300'> " + "\r\n";
            scr = scr + @"</embed></object>" + "\r\n";
           
            return scr;
        }

    }

To use it in our MainWindow.xaml we add the UserControl and bind the VideoID in our HtmlText Property.

C#
<mw:CustomWebbrowserControl Width="Auto" Height="300" HtmlText="{Binding VideoID,Mode=OneWay}"></mw:CustomWebbrowserControl>

Add SelectionChangedEvent to ListView

To comply to the conditions of MVVM the default SelectionChangedEvent is useless for us.

The Windows.System.Interactivity namespace contains an EventTrigger and we could use

the EventToCommand Action to notify our ViewModel that the selection has been changed.

This is usually indended for Expression Blend but it's well working in XAML, no need to install Blend, the DLL-File could be found in the BlendSDK.

C#
 <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <cmd:EventToCommand Command="{Binding Path=ControlListView, Mode=OneWay}" CommandParameter="{Binding Path=SelectedItem, ElementName=controlListView}" PassEventArgsToCommand="True"/>
                    </i:EventTrigger>
</i:Interaction.Triggers>

In our XAML, the Command binds to an ICommand interface in our ViewModel to Handle the action, instances the RelayCommand and send it as object to our SelectionChangedEvent-event.

C#
public ICommand ControlListView
{
      get
     {
           return _cntrolListView ?? (_cntrolListView = new RelayCommand<object>(SelectionChangedEvent)); 
     }
}
C#
private void SelectionChangedEvent(object param)
{
            try
            {
                SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
                //check if at least one object is "loaded" 
                if (e.AddedItems.Count > 0)
                    selected_data = (you_tube_data)e.AddedItems[0];
            }
            catch (IndexOutOfRangeException ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            try
            {
                //Get Comments for selected video
                Video video = reqeuest.Retrieve<Video>(new Uri("http://gdata.youtube.com/feeds/api/videos/" + selected_data.video_id));
                Feed<Comment> comment = reqeuest.GetComments(video);
                VideoComments.Clear();
                int comments_count = 0;
                foreach (Comment c in comment.Entries)
                {
                    //show only ten comments
                    if (comments_count == 10)
                        break;
                    if (c.Content != null)
                    {
                        VideoComments.Add(c.Content);
                        comments_count++;
                    }
                }
            }
            catch (GDataRequestException ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }

            try
            {
                if (selected_data.video_id != null)
                    VideoID = selected_data.video_id;
                if (selected_data.video_summary != null)
                    VideoSummary = selected_data.video_summary;
                if (selected_data.video_thumb_url != null)
                    VideoThumbURL = selected_data.video_thumb_url;
                if (selected_data.video_title != null)
                    VideoTitle = selected_data.video_title;

                RaisePropertyChanged("VideoComments");
                RaisePropertyChanged("VideoID");
                RaisePropertyChanged("VideoSummary");
                RaisePropertyChanged("VideoThumbURL");
                RaisePropertyChanged("VideoURL");
                RaisePropertyChanged("VideoTitle");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
            }
           
}

Open a new Window/inform MainViewModel with Messenger

We would like to receive our YouTube-Playlists in a new Window.

When the user selects a playlist in that new Window, we're going to inform our MainViewModel to get the Video Feeds of the selected playlist.

C#
<Button Command="{Binding ShowPlaylists}" Content="My Playlists" Width="75" Background="Transparent" Foreground="White"></Button>

In MainViewModel, we add a new RelayCommand "ShowPlaylists":

C#
ShowPlaylists = new RelayCommand(OpenPlayListWindow); 

We also register a new Action with the MVVM-Light Messenger, as a parameter we're passing a string that contains the name of our selected playlist:

C#
Messenger.Default.Register<String>(this, (action) => onReceiveMessage(action));

When our MainViewModel receives the massage, following method will be executed to receive video feeds of that playlist:

C#
private object onReceiveMessage(string Playlist)
        {
            Feed<Playlist> pl_feeds = YouTube_Helper.reqeuest.GetPlaylistsFeed("USERNAME");
            if (pl_feeds.Entries != null)
            {
                try
                {
                    string thumb_url = "";
                    detail_list.Clear();
                    foreach (Playlist pl in pl_feeds.Entries)
                    {
                        if (pl.Title == Playlist)
                        {
                            Feed<PlayListMember> video_list = YouTube_Helper.reqeuest.GetPlaylist(pl);
                            
                                foreach (Video entry in video_list.Entries)
                                {
                                    foreach (MediaThumbnail thumbnail in entry.Thumbnails)
                                    {
                                        thumb_url = thumbnail.Url;
                                    }
                                    detail_list.Add(new you_tube_data { video_description = entry.Description, video_uploader = "Uploader: " + entry.Uploader, video_view_count = "Views: " + entry.ViewCount.ToString(), video_title = entry.Title, video_summary = entry.Description, video_thumb_url = entry.Media.Thumbnails[0].Url, video_id = entry.VideoId });

                                }
                            
                        }
                    }

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.ToString());
                }
            }
            return null;
        }

In our PlaylistViewModel, we need our SelectionChangedEvent again to inform the MainViewModel:

C#
private void SelectionChangedEvent(object param)
        {
            SelectionChangedEventArgs e = (SelectionChangedEventArgs)param;
            //check if at lease one object parameter is "loaded" 
            if (e.AddedItems.Count > 0)
                selected_data = (playlist_data)e.AddedItems[0];
            Messenger.Default.Send<String>(selected_data.pl_title);  

        }

History

First Version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Sergio Andrés Gutiérrez Rojas27-Feb-12 9:26
Sergio Andrés Gutiérrez Rojas27-Feb-12 9:26 
GeneralRe: My vote of 5 Pin
El_Codero27-Feb-12 9:30
El_Codero27-Feb-12 9:30 
GeneralMy vote of 4 Pin
Dean Oliver27-Feb-12 8:13
Dean Oliver27-Feb-12 8:13 
GeneralRe: My vote of 4 Pin
El_Codero27-Feb-12 8:21
El_Codero27-Feb-12 8:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.