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:
Make two HTTP requests to YouTube's API for each feed (two feeds requested once each).
Make 50 image download requests for the thumbnails (2 feeds × 25 videos each = 50 images).
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).
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).
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.
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.
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.