Putting It All Together

At this point, you've seen how we can:

  • Convert YouTube's API XML into objects

  • Download a YouTube video and a YouTube image thumbnail

  • Convert a YouTube video into WMV and MP4 formats

  • Sync videos to iTunes and Zune

What we now want to do is combine those four tasks and make them run asynchronously, so that we can still use our application while we're downloading videos. To do all of the tasks just listed asynchronously, we'll use the InnerTubeFeedWorker class.

To give you an idea of the size of the asynchronous operations that InnerTube needs to do, let's walk through an example set of feeds that an InnerTube user might have:

Top Rated Videos of All Time

This represents the top-rated videos that have ever been on YouTube.

Most Viewed Videos This Week

This represents the most viewed videos for the current week.

That means that, at a high level, InnerTubeFeedWorker needs to do the following:

  1. Make two HTTP requests to YouTube's API for each feed (two feeds requested once each).

  2. Make 50 image download requests for the thumbnails (2 feeds × 25 videos each = 50 images).

  3. Make 50 session token requests to get a valid token to download a video (2 feeds × 25 videos = 50 requests), and then make another 50 download video requests for the FLV videos (2 feeds × 25 videos each = 50 video files).

  4. Convert all 50 videos from the Flash format to Windows Media Video (WMV) format. If the user has opted to sync with iTunes, this would mean 100 video conversions because each video would need to be converted to both WMV format (InnerTube requires this) and the MP4 format (iTunes requires this).

  5. Sync the newly converted videos to iTunes and Zune.

Because of the scale of these tasks, InnerTube is designed to run in the background and uses SmartThreadPool, a www.codeproject.com library built by Ami Bar that enables you to build and control multiple thread pools.

To put this all together, look at Figure 4-10, which shows the five main tasks involved.

  • The Update Feed Pool task updates each of the feeds. Because these requests are relatively small (3–10K), we leave three threads to work on these tasks, as each thread will complete relatively quickly.

  • The Download Video Pool task is where we download the thumbnail images and videos. As this task is a long-running task that could potentially download hundreds of megabytes, we will spin up five threads to handle the work. This task tends to be the most network- and disk-intensive, as it is pulling large files and saving them on disk.

  • The Convert Video Pool task runs once we've downloaded all the videos. This task is very CPU intensive, and you will see your machine crank up to 100% utilization with this task. Because of this, we use only two threads to convert video files.

  • The Update Master List task updates our in-memory collection of videos that we will use for our application UI.

  • The Sync to iTunes/Zune Task adds the newly converted videos to your iTunes and/or Zune music collection.

The tasks done by the InnerTubeFeedWorker class

Figure 4-10. The tasks done by the InnerTubeFeedWorker class

Note

Rather than duplicate the documentation in Ami Bar's CodeProject.com article where he walks through how to use the library, here we show how to call the InnerTubeFeedWorker background worker. For detailed documentation on SmartThreadPool, visit http://www.codeproject.com/KB/threads/smartthreadpool.aspx.

Calling InnerTubeFeedWorker

As you can see in the upcoming code, instead of using the new keyword to create an instance of InnerTubeFeedWorker, instead we use the static GetInstance method, which will retrieve an instance of the InnerTubeFeedWorker class. This is done so that there is only one instance of InnerTubeFeedWorker at one time and to prevent bad things from happening, such as multiple attempts to download and convert videos on different threads, as each thread would attempt to write to the same file stream, resulting in exceptions or data corruption.

When we call GetInstance, we pass in the list of InnerTubeFeeds and our application settings. We will cover the App class and the Setting class later in the Global variables section.

We can also set up event handlers for InnerTubeFeedWorker when it reports progress and when it's finished executing, as shown in Example 4-47 and Example 4-48.

Example 4-47.  C# code for InnerTubeFeedWorker event handlers

InnerTubeFeedWorker iWork = InnerTubeFeedWorker.GetInstance(App.InnerTubeFeeds,
  App.Settings);

iWork.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(
  iWork_ProgressChanged);
iWork.RunWorkerCompleted += new System.ComponentModel.
 RunWorkerCompletedEventHandler(iWork_RunWorkerCompleted);

Example 4-48.  Visual Basic code for InnerTubeFeedWorker event handlers

Dim iWork As InnerTubeFeedWorker = InnerTubeFeedWorker.GetInstance( _
  App.InnerTubeFeeds, App.Settings)

AddHandler iWork.ProgressChanged, AddressOf iWork_ProgressChanged
AddHandler iWork.RunWorkerCompleted, AddressOf iWork_RunWorkerCompleted

InnerTubeFeedWorker has a WorkType enumeration that will tell what specific tasks InnerTubeFeedWorker should execute, as explained in the code comments in Example 4-49 and Example 4-50.

Example 4-49.  C# code for the types of work the InnerTubeFeedWorker class can do

public enum WorkType
{
  UpdateFeeds, //just Update YouTube API feeds
  Download, //just download FLV files
  DownloadAndConvert, //download and convert FLV files
  Convert, //just convert FLV files
  All //do all of the above
}

Example 4-50.  Visual Basic code for the types of work the InnerTubeFeedWorker class can do

Public Enum WorkType
  UpdateFeeds 'just Update YouTube API feeds
  Download 'just download FLV files
  DownloadAndConvert 'download and convert FLV files
  Convert 'just convert FLV files
  All 'do all of the above
End Enum

To actually start the background worker, we need to call the RunWorkerAsync method and pass in the WorkType enum we want it to complete. This method will execute immediately, with all of the processing for InnerTubeFeedWorker taking place on separate, background threads.

To receive progress updates from InnerTubeFeedWorker, you can read e.UserState, which will contain a string with progress updates, such as when a video download is starting, when it's complete, and so forth, as shown in Example 4-51 and Example 4-52. Similarly, when InnerTubeFeedWorker finishes executing, it will return an updated list of InnerTubeFeeds with which we can cast and update our InnerTubeFeeds variable.

Example 4-51.  C# code to read progress changed events

void iWork_ProgressChanged(object sender,
  System.ComponentModel.ProgressChangedEventArgs e)
{
  string s = (string)(e.UserState);
...

void iWork_RunWorkerCompleted(object sender,
  System.ComponentModel.RunWorkerCompletedEventArgs e)
{
  App.InnerTubeFeeds = (ObservableCollection<InnerTubeFeed>)e.Result;
...

Example 4-52.  Visual Basic code to read progress changed events

Private Sub iWork_ProgressChanged(ByVal sender As Object, _
  ByVal e As System.ComponentModel.ProgressChangedEventArgs)
  Dim s As String = CStr(e.UserState)
...

Private Sub iWork_RunWorkerCompleted(ByVal sender As Object, _
  ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
  App.InnerTubeFeeds = CType(e.Result, ObservableCollection(Of InnerTubeFeed))

Get Coding4Fun now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.