Skip to main content

Tutorial: Most popular YouTube videos of the day with Actionscript 3

·16 mins

As you probably know, Flash CS3 is coming really soon. Actionscript has developed a lot with the years and just like Adobe’s Flex, Flash CS3 will feature it’s latest revision, actionscript 3.0. This tutorial is a pretty basic introduction to AS3, to give you a kickstart before the final release of Flash CS3. If you’re familiar with AS3, this probably isn’t anything for you, but if you’ve done some stuff in AS1 & 2, maybe had a look at 3, read it and tell me what you think 🙂 This was originally a school assignment and written pretty quickly so please bear with me 😇

Alrighty, let’s start.

Before you build a house you need to get the tools so let’s take a look at a couple approaches of working in AS3:

Currently there are three ways.

  • Flash 9 alpha
  • Flex
  • Flex SDK with third party application

Flash 9 alpha is basically Flash 8, but with support and a compiler for AS3. You can download it for free from Adobe labs, but you’ll need a licenced Flash 8 to install it. Also it does not work on intel macs so if you have one, you can’t compile your actionscript with it. This obviously is a problem 😄

Flex is basically flash, but meant more for online applications. Supports the exact same actionscript 3 as Flash 9. If you have flex, you probably already know that you can compile AS3 with it, if you don’t you can always get it. It’s $499 though

The Flex SDK is a free download which you can use with a third party application. I myself use this one together with Eclipse, and am very happy with the combination. Free too 😃

You’ll find loads of info about these on google, and going thru all this isn’t really in the scope of this tutorial, so please, help yourself 🙂

Very helpful links:

Here’s a small demonstration of what the app we’re building today might look like:

Screenshot showing popular today on YouTube

What it does it to read the RSS feed from YouTube, parse it, create buttons that link to the 25 most recent videos and show thumbnails from those videos. I chose this example because it shows the power of E4X, which is now implemented in AS3, makes our lives a lot easier when it comes to parsing XML. It also has some basic events and connections over the web, which also have changed significantly since AS2. So, an easy AS2 to AS3 migration tutorial. Or something.

So, where to start. Let’s plan a bit first. Planning is always good. What do we want to do really? Here’s a list

  1. Load the XML from YouTube
  2. Parse the XML
  3. Create buttons of the items in the XML
  4. Load thumbnails for the images
  5. Sleep 😄

Ok, now we know what we want to do, and also looks like the list in a pretty logical order, which is nice 🙂

After this we have to plan how we want to structure the project. Jumping to code too soon is a mistake waaay too many make in the beginning. First decide what you want your application to do (hey, we just did that, simplified), then go to the application design phase where you design the application architecture (graphical design is something i like to bring in pretty early too, but that’s just because working with pretty things is nicer than working with .. ugly stuff 😜). Anyway, let’s plan a bit how the application will be set up.

Since this is a pretty simple application we won’t make too too complex with classes either. I chose to put the XML in the main class, just to make a life a bit easier (or more accurately faster, in this case)

I’ve chosen to have this divided to three classes

  • Main - The base for everything, also handles XML
  • ui.VideoChart - A grid with the buttons, places the buttons and handles load queuing for images
  • ui.Thumbnail - A button with a thumbnail from the video with built in image loader

The ui is the package, so they will have to be in a folder called ui, just like in AS2

I’ll now go through all these classes, one by one, explaining what I’m doing. I’ll first paste the full source of the class, so that it’s easier for you to follow.

Because of YouTube’s crossdomain policy not allowing us to access their data, i’m using a crossdomain proxy for this. What we do is basically send the request to a server of our own, which will run a script that downloads the data needed from YouTube and then forward the data to us, with a crossdomain policy that’s ok for us. This causes the webserver to download every file, causing a lot of extra bandwidth. Keep that in mind. The crossdomain proxy script used in this example is included with the source.


Main.as

package {
   
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.text.*;
   
    import ui.*;
   
    public class Main extends Sprite {
       
        private var youTube_developerId  : String = "YOUR DEVELOPER ID";
        private var crossdomain_proxy      : String = "http://www.anttikupila.com/crossdomain-proxy.php?url=";
       
        private var logo                    : TextField;
        private var xmlLoader            : URLLoader;
        private var videoChart        : VideoChart;
 
        public function Main() {
           
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
 
            logo = new TextField( );
            logo.selectable = false;
            logo.antiAliasType = AntiAliasType.ADVANCED;
            logo.autoSize = TextFieldAutoSize.LEFT;
               
            var logoFormat : TextFormat = new TextFormat( );
                logoFormat.font = "Arial";
                logoFormat.color = 0x999999;
                logoFormat.size = 36;
               
            logo.defaultTextFormat = logoFormat;
            logo.text = "Popular today on YouTube";
            logo.x = 20;
            logo.y = 20;
           
            xmlLoader = new URLLoader( );
            xmlLoader.addEventListener( Event.COMPLETE, xmlLoadHandler );
            xmlLoader.load( new URLRequest( crossdomain_proxy + "http://www.youtube.com/api2_rest?method=youtube.videos.list_popular&dev_id=" + youTube_developerId + "&time_range=day" ) );
           
            addChild( logo );
           
        }
       
        // EVENT HANDLERS
       
        private function xmlLoadHandler( e : Event ) : void {
           
            var films : Array = new Array( );
            var index : int = 0;
           
            for each( var i : XML in new XML( e.target.data ).video_list.video ) {
           
                var film : Object = new Object( );
                    film.thumbnail = i.thumbnail_url;
                    film.url = i.url;
                films.push( film );
            }
            xmlLoader.removeEventListener( Event.COMPLETE, xmlLoadHandler );
           
            videoChart = new VideoChart( films );
            videoChart.x = 15;
            videoChart.y = logo.y + logo.textHeight + 20;         
           
            addChild( videoChart );
           
        }
       
    }
   
}

If you don’t know any AS, this might seem a bit confusing, but when you break it down, it’s really not.

package {
   
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.text.*;
   
    import ui.*;
   
    public class Main extends Sprite {

In actionscript 3 you have to define everything inside packages. More on this later. We then define which classes we need to import for this class. Normally you obviously add these as required. Organizing them by package is good coding practice, but you do as you like. After this we define the class and make it extends the Sprite class, which is pretty much a MovieClip, but without the timeline.

private var youTube_developerId  : String = "YOUR DEVELOPER ID";
private var crossdomain_proxy      : String = "http://www.anttikupila.com/crossdomain-proxy.php?url=";
       
private var logo                    : TextField;
private var xmlLoader            : URLLoader;
private var videoChart        : VideoChart;

We define the variables used in this class. The youTube_developerId is something you’ll have to get yourself from YouTube. This is an API key required to access YouTube’s application programming interface. Get your API key from here.

public function Main() {
   
    stage.scaleMode = StageScaleMode.NO_SCALE;
    stage.align = StageAlign.TOP_LEFT;
 
    logo = new TextField( );
    logo.selectable = false;
    logo.antiAliasType = AntiAliasType.ADVANCED;
    logo.autoSize = TextFieldAutoSize.LEFT;
       
    var logoFormat : TextFormat = new TextFormat( );
        logoFormat.font = "Arial";
        logoFormat.color = 0x999999;
        logoFormat.size = 36;
       
    logo.defaultTextFormat = logoFormat;
    logo.text = "Popular today on YouTube";
    logo.x = 20;
    logo.y = 20;
   
    xmlLoader = new URLLoader( );
    xmlLoader.addEventListener( Event.COMPLETE, xmlLoadHandler );
    xmlLoader.load( new URLRequest( crossdomain_proxy + "http://www.youtube.com/api2_rest?method=youtube.videos.list_popular&dev_id=" + youTube_developerId + "&time_range=day" ) );
   
    addChild( logo );
   
}

Ok, a bit more this time, but no to worry! Let’s break it down, bit by bit. Since this function is called exactly the same as the class, it’s called a constructor function, and is executed when the class is created. Basically this is the first code that’s ran.

We first set a couple parameters to the stage to make it align in the top left corner and not scale at all. You AS2 users: notice the constants used here, no ugly strigs, but predefined constants instead. A new thing to to AS3.

After this we create a new TextField. This will be used for the logotype of the app. It’s pretty self explanatory so i won’t explain it close here. Google is your friend 😉

The URLLoader however maybe doesn’t look too familiar, and it isn’t if you’re an pre-as3 user. In actionscript 3 most external loads, excluding images and such, go through a class called URLLoader. We create it, and add an event listener. Again we have a constant for the event itself. We bind the event to a function we’ll create later called xmlLoadHandler. This function will be executed with the event when the load has been completed. After this we load a new URLRequest, also new to AS3 with the path to the YouTube REST service. Check YouTube developer documentation for more info.

So, we have now created a new TextField, but it’s not visible. Hmm .. attachMovie maybe? createEmptyMovieClip? Nope, in AS3 you just add the created display object to the display stack. In this case, since this class extends Sprite, we can just add the TextField to this class’s display stack. We should now see a textfield on the stage if we compile. Cool :)

Time to move on. The YouTube XML has been loaded and xmlLoadHandler will fire.

// EVENT HANDLERS
 
private function xmlLoadHandler( e : Event ) : void {
   
    var films : Array = new Array( );
    var index : int = 0;
   
    for each( var i : XML in new XML( e.target.data ).video_list.video ) {
   
        var film : Object = new Object( );
            film.thumbnail = i.thumbnail_url;
            film.url = i.url;
        films.push( film );
    }
    xmlLoader.removeEventListener( Event.COMPLETE, xmlLoadHandler );
   
    videoChart = new VideoChart( films );
    videoChart.x = 15;
    videoChart.y = logo.y + logo.textHeight + 20;         
   
    addChild( videoChart );
   
}

We first create a new array which will store all the movies in an easy-to-access list.

Now comes the exciting part, parsing the xml. Actionscript 3 has E4X built in, which is really effective. You can now forget about all the firstChild.childNodes[3].childNodes[2].firstChild nightmares, with E4X xml is child’s play. Let’s see what happens here.

e is the event and target is the target of the event, the one that fired it. This way we have access back to the URLLoader. Content is the actual data. We create and XML object out of this data. Normally you’d now have to start looping etc to find what you want but with actionscript 3 we can simply say .video_list to access that node and .video to get a list of all xml nodes inside that node! Super effective! Also, with the new for each loop we can store the current item in the loop in a temporary variable, for easy & quick access. Smart. After this we create an object and store the items inside that object. We then push that object to the array, so that we have a “database” of items inside flash. That’s it!

Just for comparison, here’s the exact same code in actionscript 2:

function xmlLoadHandler( ) : void {
 
    var films : Array = new Array( );
 
    for ( var i : Number = 0; i <xml.firstChild.firstChild.childNodes.length; i++) {
        if (xml.firstChild.firstChild.childNodes[ i ].nodeName == "video") {
 
            var film : Object = new Object( );
 
            for ( var j : Number = 0; i <xml.firstChild.firstChild.childNodes[ i ].childNodes.length) {
                if (xml.firstChild.firstChild.childNodes[ i ].childNodes[ j ].nodeName == "thumbnail_url") {
                    film.thumbnail = xml.firstChild.firstChild.childNodes[ i ].childNodes[ j ].firstChild.nodeValue;
                }
                if (xml.firstChild.firstChild.childNodes[ i ].childNodes[ j ].nodeName == "url") {
                    film.url = xml.firstChild.firstChild.childNodes[ i ].childNodes[ j ].firstChild.nodeValue;
                }
            }
            films.push( film );
        }
    }
    // [ Additional code for adding the VideoChart ]
}

Messy? Yep. Luckily this is all over with AS3. After this we create a VideoChart class, and pass the array of items to it. We then add, just like the logo, the VideoChart to the display stack.


VideoChart.as

package ui {
   
    import flash.display.*;
    import flash.events.*;
   
    import ui.*;
   
    public class VideoChart extends Sprite {
       
        private var xSpacing            : int = 10;
        private var ySpacing            : int = 10;
        private var columns    : int = 5;
        private var thumbnails      : Array = new Array ( );
        private var loadedThumbnails    : Array = new Array ( );
       
        function VideoChart( films : Array ) {
           
            var col : int = 0;
            var row : int = 0;
           
            for each ( var film : Object in films ) {
               
                var thumb : Thumbnail = new Thumbnail( film.thumbnail, film.url );
                    thumb.x = col * thumb.width + col * xSpacing;
                    thumb.y = row * thumb.height + row * ySpacing;
                    thumb.addEventListener( Event.COMPLETE, thumbLoadedHandler );
               
                thumbnails.push( thumb );
                addChild( thumb );
               
                col++;
               
                if ( col>= columns ) { 
                    col = 0;
                    row++;
                }
               
            }
           
            loadNextThumbnail( );
           
        }
       
        private function loadNextThumbnail( ) : void {
           
            if ( loadedThumbnails.length <thumbnails.length ) thumbnails[ loadedThumbnails.length ].init( );
           
        }
       
        // EVENT HANDLERS
       
        private function thumbLoadedHandler( e : Event ) : void {
               
            loadedThumbnails.push( thumbnails[ loadedThumbnails.length ] );
            loadNextThumbnail( );
               
        }
       
    }
   
}

The beginning is pretty much the same as in Main. Notice the package. Let’s jump straight to the constructor

function VideoChart( films : Array ) {
   
    var col : int = 0;
    var row : int = 0;
   
    for each ( var film : Object in films ) {
       
        var thumb : Thumbnail = new Thumbnail( film.thumbnail, film.url );
            thumb.x = col * thumb.width + col * xSpacing;
            thumb.y = row * thumb.height + row * ySpacing;
            thumb.addEventListener( Event.COMPLETE, thumbLoadedHandler );
       
        thumbnails.push( thumb );
        addChild( thumb );
       
        col++;
       
        if ( col>= columns ) { 
            col = 0;
            row++;
        }
       
    }
   
    loadNextThumbnail( );
   
}

First we define two integers as 0, so that we can increase them later on. Notice the int, instead of Number. Int is for whole numbers (1,2,3,4 and so on), while Number still exists for floats. You should use int everywhere where you know you’ll only have numbers without decimals as it’s a lot faster.

We then loop through the array we got in from Main and create new Thumbnail classes for every item. We’ll come to the Thumbnail class a bit later. We then position the thumbnails using really advanced elementary school math 😅 We’ll want to know when the image of the thumbnail has loaded (so that we know when to start loading the next one) so a listener is a good idea. We add the thumbnail to the array, so that we can keep track of all the thumbnails, which is required for our load queue to work (we want to load only one image at a time). We then run the loadNextThumbnail() function, which will load the next thumbnail, or in this case, the first one.

private function loadNextThumbnail( ) : void {
   
    if ( loadedThumbnails.length <thumbnails.length ) thumbnails[ loadedThumbnails.length ].init( );
   
}

That was a short one. Basically it checks if the list of loaded thumbnails is shorter than the list of all thumbnails (meaning that there are still thumbnails left to load) and loads the next thumbnail. The thumbnail itself will handle the loading, we’ll just have to tell it when to do it.

Not much more to the VideoChart really, let’s move on to the Thumbnail


Thumbnail.as

package ui {
   
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
 
    public class Thumbnail extends Sprite {
       
        private var imageLoader    : Loader;
        private var _image          : String;
        private var _url                    : String;
   
        function Thumbnail( image : String, url : String ) {
           
            imageLoader = new Loader( );
            imageLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, loadCompleteHandler );
           
            _image = image;
            _url = url;
           
            var background : Shape = new Shape( );
                background.graphics.beginFill( 0xEEEEEE );
                background.graphics.drawRoundRect( 0, 0, 140, 103, 10, 10 );
                background.graphics.endFill( );
           
            buttonMode = true;
            useHandCursor = true;
               
            addEventListener( MouseEvent.CLICK, clickHandler );
           
            addChild( background );
        }
       
        public function init( ) : void {
            imageLoader.load( new URLRequest( "http://www.anttikupila.com/crossdomain-proxy.php?url=" + _image ) );
        }
       
        // EVENT HANDLERS
       
        private function loadCompleteHandler( e : Event ) : void {
           
            dispatchEvent( new Event( Event.COMPLETE ) );
           
            var image : Bitmap = imageLoader.content as Bitmap;
                image.x = 5;
                image.y = 5;
            addChild( image );
           
        }
       
        private function clickHandler( e : Event ) : void {
       
            navigateToURL( new URLRequest( _url ), "_blank" );
           
        }
       
    }
   
}

Again the beginning is pretty much obvious, or by this point old, so i’ll skip it.

function Thumbnail( image : String, url : String ) {
   
    imageLoader = new Loader( );
    imageLoader.contentLoaderInfo.addEventListener( Event.COMPLETE, loadCompleteHandler );
   
    _image = image;
    _url = url;
   
    var background : Shape = new Shape( );
        background.graphics.beginFill( 0xEEEEEE );
        background.graphics.drawRoundRect( 0, 0, 140, 103, 10, 10 );
        background.graphics.endFill( );
   
    buttonMode = true;
    useHandCursor = true;
       
    addEventListener( MouseEvent.CLICK, clickHandler );
   
    addChild( background );
}

We first create a Loader class, which is used for loading images with AS3. We’ll listen to the loaders onComplete event so that we can tell VideoChart that we’re done and it can load the next image. When we create a new Thumbnail, we’ll receive two parameters, an image (url) and an url (link to the youtube page), we’ll save these as instance variables, so that they can be accessed when needed.

We then create a simple background shape, using AS3’s new drawRoundRect, which will draw a rounded rectangle. I’m sure that’ll come in handy several times 😉 Notice that we now access the drawing API through the graphics, not directly.

In order for this thumbnail to have a hand cursor we’ll make it a button and well, make it use a hand cursor 🙂

We then listen for mouse clicks and run the clickHandler when the user clicks an item. After this we add the background to the display stack of the thumbnail.

public function init( ) : void {
    imageLoader.load( new URLRequest( "http://www.anttikupila.com/crossdomain-proxy.php?url=" + _image ) );
}
 
// EVENT HANDLERS
 
private function loadCompleteHandler( e : Event ) : void {
   
    dispatchEvent( new Event( Event.COMPLETE ) );
   
    var image : Bitmap = imageLoader.content as Bitmap;
        image.x = 5;
        image.y = 5;
    addChild( image );
   
}

We load an image and when the image has loaded loadCompleteHandler will fire, with the event itself as a parameter (just like the URLLoader, remember?). After this we’ll store the data received in a Bitmap object, move it a bit (for the rounded corner) and add it to the display stack.

private function clickHandler( e : Event ) : void {
 
    navigateToURL( new URLRequest( _url ), "_blank" );
   
}

Ok, final one! When the user clicks the button, this handler will execute. All we need to do is to open the page in a new window, no biggie. Notice that getURL is gone, instead we have navigateToURL (which makes a lot more sense!). Also, you have to send the url wrapped inside a URLRequest object.


That’s pretty much it. This is just a quick introduction and i hope you’ll get the spark to explore, experiment and learn. Actionscript 3 is what actionscript should have been from the start. It’s more of a real programming language, where actionscript 2 is some kind of an attempt on true OOP, but not quite perfect. The actionscript 3 virtual machine is also a lot faster.

At the time of writing Flash CS3 hasn’t been released, but will be soon. We all know it’ll rock. Hard.

Have a good one, i hope you learned something!

The entire project’s source can be downloaded from here