Home >
Welcome back to our series. In the previous article, we discussed the elements that are the necessary building blocks of the application MovieExpert (http://apps.facebook.com/movieexpert/). We created the main swf, we created the invite screen, we worked hard to make the invite screen actually work on our iFrame. Now that we have all those details done, we can move on and concentrate entirely on the application in the swf file. We know that the invite screen on facebook appears only when the use installs the application, the next time he logs in, he will be redirected directly to http://apps.facebook.com/movieexpert/.
Well, one of the most important part in this application, that drives the whole logic, is the integration of YouTube videos right inside the swf. In the least article we already discussed what library we will use for this idea. As the first step towards this goal, we will implement a simple YouTube video loader that will be customized later on. We need to implement all those functions like Play, Pause, Stop, Seek etc. All those functions are part of YouTube video controls, we cannot simply let the player watch the video without being able to jump to certain video sections, start the video, pause the video etc.
So, let’s start with the development. Open Flash and the open the main.fla we created in the previous article. Here is the screenshot:
Ok, the size of swf is set to 700 x 450 px. The big enough to hold all the controls we have.
Inside the working project folder of your application, create a folder called "classes".
Inside the classes folder, paste the “choppingblock” folder inside the "classes” folder, which you can find the the downloaded YouTube ActionScript library from the previous article. Here is the screenshot:
Right now we have the right classes set up, the next we need to do is to create a test the class by loading a video right from YouTube. Let's go and check what video's we can load. One of my favorite movies is certainly Gladiator, so I found a clip from the movie on YouTube, the URL is http://www.youtube.com/watch?v=lPgxsGRpiuw. Check out the scene and if you haven’t seen the movie yet, I recommend you take a look at it.
The scene from the movie suits perfectly our needs since it contains goofs made by the movie makers. I think now it's not the right moment to talk about where the goof is, we need to concentrate on the integration of the video in our swf.
So, now that we have a cool scene to work with, it will not be boring at all to work on this part. For me, as Gladiator fan, it's a lot of fun to watch scenes from my favorite movie and to work with ActionScript. I encourage you to search for other movie scenes from other movies that you might find interesting, if Gladiator is not the movie you prefer :-).
Let's get back to the main fla we worked on.
We need to be very careful now. For now, the goal is to load the video (it may not be on facebook), nothing more, no controls. We need to create 2 layers on the main timeline. Like on the image:
The upper image is named "Actions", the lower image "Assets". Select the first frame and open the actions panel:
First we need to import the right classes:
import classes.*;
import classes.choppingblock.video.*;
//instaniate the class
var videoHolder = new YouTubeLoader();
//create the video holder
videoHolder.create();
//add the child
addChild(videoHolder);
//load the video by id
videoHolder.loadVideoById("lPgxsGRpiuw");
When compiled, not uploaded, the result is as follows:
Cool, we have on the right path. The video will load but will not play, even by calling the play() function. It's not our goal right now to make it play, the goal is to make sure you understand the process of YouTube video integration into the swf. Now, unfortunately there seems to be an error and there is no appropriate event that tells us when we can call the play() method to actually play the video. No matter, there is a workaround for this problem but will be covered later.
Extending the Youtube Video and adding controls
Now it’s time to actually create video controls. Also, we will need to extend the existing YouTubeLoader class to add some fancy features. We need to add the play and pause button, also we need the seek bar to let the players seek from one video moment to another very quickly. I think there is no ned now for a stop button (maybe I'm wrong, don't blame me). Here is how the controls look like:
Before all the magic with the controls, we need to extend the existing YouTubeLoader to provide the additional features we need. The class will be called YouTubeLoaderPlus and here is how the code looks like:
package classes.choppingblock.video{
import flash.events.*;
import flash.display.*;
import classes.*;
public class YouTubeLoaderPlus extends YouTubeLoader {
public static var PLAYER_READY:String = "playerReady";
public static var VIDEO_READY:String = "videoReady";
protected var _count:Number = 0;
function YouTubeLoaderPlus() {
super();
this.addEventListener(Event.ENTER_FRAME, checkPlayerReady);
this.addEventListener(YouTubeLoaderPlus.PLAYER_READY, ytPlayerReady);
this.addEventListener(YouTubeLoaderPlus.VIDEO_READY, ytVideoReady);
}
private function ytPlayerReady(e:Event) {
this.setSize(450, 370);
}
private function ytVideoReady(e:Event) {
this.pause();
}
private function checkPlayerReady(e:Event) {
_count++;
if(_count >= 50){
this.removeEventListener(Event.ENTER_FRAME, checkPlayerReady);
dispatchEvent(new Event(YouTubeLoaderPlus.PLAYER_READY));
}
}
private function checkVideoReady(e:Event) {
if(this.getDuration()){
this.removeEventListener(Event.ENTER_FRAME, checkVideoReady);
dispatchEvent(new Event(YouTubeLoaderPlus.VIDEO_READY));
}
}
override public function loadVideoById (id:String, startSeconds:Number = 0):void{
super.loadVideoById(id, startSeconds);
this.addEventListener(Event.ENTER_FRAME, checkVideoReady);
}
};
};
Save the class as YouTubeLoaderPlus.as in the same folder where YouTubeLoader lives, that’s classes\choppingblock\video. The purpose of this class is simply to provide signals when the video is ready to be used in the swf. Go to the first frame of the fla and place this new code inside:
var videoHolder = new YouTubeLoaderPlus();
videoHolder.create();
addChild(videoHolder);
videoHolder.loadVideoById("lPgxsGRpiuw");
videoHolder.addEventListener(YouTubeLoaderPlus.PLAYER_READY, ytPlayerReady);
videoHolder.addEventListener(YouTubeLoaderPlus.VIDEO_READY, ytVideoReady);
function ytPlayerReady(e:Event):void{
trace("Player is ready...");
}
function ytVideoReady(e:Event):void{
trace("Video is ready...");
}
Let’s see how this looks like in the facebook canvas window. The goal is to see the trace messages in the window that the player and the video is ready. So, let’s compile the swf and run it inside the facebook canvas. The result on my side:
Cool, as we can see, there is the video and also in the left part of the screen, we see that the events have been fired! We can see that the video and the player are ready to interact with the application. Now we should check if the play() method actually works. Here is how the code looks like in the first frame:
import classes.*;
import classes.choppingblock.video.*;
var videoHolder = new YouTubeLoaderPlus();
videoHolder.create();
addChild(videoHolder);
videoHolder.addEventListener(YouTubeLoaderPlus_Test.PLAYER_READY, ytPlayerReady);
videoHolder.addEventListener(YouTubeLoaderPlus_Test.VIDEO_READY, ytVideoReady);
function ytPlayerReady(e:Event):void{
videoHolder.loadVideoById("lPgxsGRpiuw");
}
function ytVideoReady(e:Event):void{
videoHolder.play();
}
So, what happens? Here is the image of my application:
Yes, Gladiator finally plays inside Facebook! Just enjoy the moment for a while.
Let’s check the code quickly:
function ytPlayerReady(e:Event):void{
videoHolder.loadVideoById("lPgxsGRpiuw");
}
function ytVideoReady(e:Event):void{
videoHolder.play();
}
In this case we clearly see that first the player needs to be ready before we load any videos. If this order is not followed, videos will not load. So, the order goes as follows: initiate the player, load the video.
Adding controls
Well, since we are able to start the clip, we need to have some sort of controls to control the clips that is loaded, here is the image of what we are going to create:
Ok, for now, let's concentrate entirely on the play and pause button. The whole control clip will be one file called YouTubeControl. Well, here is the basic class structure:
package classes.choppingblock.video{
import flash.display.*;
import flash.events.*;
public class YouTubeControl extends MovieClip{
public static var PLAY_PRESSED:String = "playPressed";
public static var PAUSE_PRESSED:String = "stopPressed";
function YouTubeControl(){
_playButton.addEventListener(MouseEvent.CLICK, playPressed);
_pauseButton.addEventListener(MouseEvent.CLICK, pausePressed);
}
private function playPressed(e:MouseEvent):void {
dispatchEvent(new Event(YouTubeControl.PLAY_PRESSED));
}
private function pausePressed(e:MouseEvent):void {
dispatchEvent(new Event(YouTubeControl.PAUSE_PRESSED));
}
}
}
Save it as YouTubeControl.as in the same folder like the other video classes, namely classes\choppingblock\video. Go back to the main.fla we worked on and create a new MovieClip called YouTubeControl.
Place two standard button components inside the clip:
Make sure you set the following linkage option, so the clip connects to the class we just created:
Name both buttons _playButton and _playButton, respectively. Right click on the clip and select linkage:
Let’s test to actually see if the control buttons work at all. Place one instance of the YouTubeControl right on the stage.
Make sure to name the instance test_mc and add the following code to the first frame, but before that comment the previous code out:
test_mc.addEventListener(YouTubeControl.PLAY_PRESSED, playPressed);
test_mc.addEventListener(YouTubeControl.PAUSE_PRESSED, pausePressed);
function playPressed(e:Event):void {
trace("play");
}
function pausePressed(e:Event):void {
trace("stop");
}
And the result is:
We just tested the clip in an isolated environment and it seems like it works. Now we are ready to integrate the component right inside the youtube loader class. But wait, there is still some things to do.
We need to create a nice SeekBar that will help users have better control over the clip. They will be able to jump from one section in the clip to other etc. Like in the image below:
it will be very similar to the original YouTube seek bar. The SeekBar itself will consist of few MovieClips nested in each other.
Here is how the code looks like for the SeekBar class:
package classes.choppingblock.video{
import flash.events.*;
import flash.display.*;
public class SeekBar extends MovieClip {
protected var _initWidth:Number = 0;
public static var BAR_CLICKED:String = "barClicked";
function SeekBar() {
super();
_initWidth = _bar.width;
_bar.addEventListener(MouseEvent.CLICK, barClicked);
}
private function barClicked(e:MouseEvent):void {
var prc = this.mouseX / _initWidth;
var ex = new SuperEvent(SeekBar.BAR_CLICKED, true, true);
ex.add("prc", prc);
dispatchEvent(ex);
}
public function setProgress(completed:Number, complete:Number) {
var prc = completed / complete;
if(prc > 0){
_bar.width = _initWidth * prc;
}
}
public function setSeekProgress(curr:Number, dur:Number) {
var prc = curr / dur;
if(prc > 0){
_seekPoint.x = _initWidth * prc;
//_bar.width = _initWidth * prc;
}
}
};
};
Save the code as SeekBar.as inside the classes/choppingblock/video.
In fact, the whole code is pretty simple, but it will get much more clear as we move on to the main.fla that contains the movie clip. Go back to main.fla. Create a new MovieClip called SeekBar:
Make sure the following linkage options are set:
The class the clip is being linked to is classes.choppingblock.video.SeekBar, that’s clear. Next thing we need to do is to jump edit the clip. Right click the clip
When we enter the editing of SeekBar, we need to create 3 payers on it’s very own timeline, like on the image below.
The top layer is named “Canvas”, the middle layer “SeekPoint”, the lower one “Bar”. We will start with the upper layer. Click on the upper layer. Draw a small rectangle (width 193 heigth 11, no fill!) on the stage.
Select the lower “Bar” movie clip and draw a small blue rectangle (filled), same size as the above rectangle.
Convert the blue shape to MovieClip and name it BarInside.
We also need to set the instance name of the clip to _bar.
That’s cool, the small bar is ready. At the end, we need a small seek point clip. Create a new movieclip with the following shape:
Place it on the middle layer.
Make sure the instance name of the clip is set to _seekPoint and the clip is set on x position 0. Well, the SeekBar is now ready to go!
The next thing we need to do here is to test if the SeekBar actuall works. So, remove all items from the stage and place one instance of the SeekBar right on the stage. Make sure the stage is empty before that and all other code from frame 1 is commented out. Name the clip seek_mc:
Now we will add some code to see how the seekbar works before we actually place it in the controls. It’s always good to test a component inside an isolated environment.
Let’s first test the setProgress(completed:Number, complete:Number) method, that will used to display the loading progress of the video. Here is the code:
seek_mc.setProgress(10, 100);
Here is the result on my side:
Wow, this looks like the whole method is working fine.
Let’s test the setSeekProgress(curr:Number, dur:Number) method. Here is the code:
seek_mc.setSeekProgress(50, 100);
Here is the result:
This method works fine also. Well, now it’s the time to actually test the event handlers that is fired as soon as we click somewhere on the seekbar. This behavoius is needed when the user wants to jump from one section to another.
Well, here is the code:
seek_mc.addEventListener(SeekBar.BAR_CLICKED, barClicked);
function barClicked(e:SuperEvent):void{
var prc = e.get("prc");
trace("prc: " + prc);
}
The result is the percentage of the mouse offset on the seek bar width. We can use the number to jump to sections in videos. Here is the result on my side and how it looks like:
Well, there is no need to explain that further. The SeekBar is tested and it looks really ok, it's ready to be integrated into the YouTubeControl class! Here is the modified code of YouTubeControl.as:
package classes.choppingblock.video{
import flash.display.*;
import flash.events.*;
public class YouTubeControl extends MovieClip{
public static var PLAY_PRESSED:String = "playPressed";
public static var PAUSE_PRESSED:String = "stopPressed";
public static var CONTROLS_HOVERED:String = "controlsHovered";
function YouTubeControl(){
_playButton.addEventListener(MouseEvent.CLICK, playPressed);
_pauseButton.addEventListener(MouseEvent.CLICK, pausePressed);
_playButton.addEventListener(MouseEvent.ROLL_OVER, rolledOver);
_pauseButton.addEventListener(MouseEvent.ROLL_OVER, rolledOver);
_seekBar.addEventListener(MouseEvent.ROLL_OVER, rolledOver);
}
private function playPressed(e:MouseEvent):void {
dispatchEvent(new Event(YouTubeControl.PLAY_PRESSED));
}
private function rolledOver(e:MouseEvent):void {
dispatchEvent(new Event(YouTubeControl.CONTROLS_HOVERED, true, true));
}
private function pausePressed(e:MouseEvent):void {
dispatchEvent(new Event(YouTubeControl.PAUSE_PRESSED));
}
public function setProgress(completed:Number, complete:Number) {
_seekBar.setProgress(completed, complete);
}
public function setSeekProgress(completed:Number, complete:Number) {
_seekBar.setSeekProgress(completed, complete);
}
}
}
Go back to the fla and open the movieclip movie control.
Place one instance of the seekbar onto the stage and give the instance name _seekBar. With that in mind, we have a functioning YouTubeControl class. Now, we need to work on how to implement that into the YouTubeControl class. Here is revised class code for YouTubeControl.as:
package classes.choppingblock.video{
import flash.events.*;
import flash.display.*;
import classes.*;
public class YouTubeLoaderPlus_Test extends YouTubeLoader {
public static var PLAYER_READY:String = "playerReady";
public static var VIDEO_READY:String = "videoReady";
protected var _count:Number = 0;
protected var _controls:YouTubeControl = new YouTubeControl();
function YouTubeLoaderPlus_Test() {
super();
this.addEventListener(Event.ENTER_FRAME, checkPlayerReady);
this.addEventListener(YouTubeLoaderPlus_Test.PLAYER_READY, ytPlayerReady);
this.addEventListener(YouTubeLoaderPlus_Test.VIDEO_READY, ytVideoReady);
this.addChild(_controls);
_controls.addEventListener(YouTubeControl.PLAY_PRESSED, playPressed);
_controls.addEventListener(YouTubeControl.PAUSE_PRESSED, pausePressed);
this.addEventListener(SeekBar.BAR_CLICKED, seekBarClicked);
trace("Stav");
}
private function seekBarClicked(e:SuperEvent):void {
var prc = e.get("prc");
this.seekTo(prc * this.getDuration());
}
private function ytPlayerReady(e:Event) {
this.setSize(450, 370);
_controls.y = 380;
}
private function ytVideoReady(e:Event) {
this.pause();
this.addEventListener(Event.ENTER_FRAME, updatePreloadStatus);
}
private function updatePreloadStatus(e:Event) {
var l = this.getBytesLoaded();
var t = this.getBytesTotal();
_controls.setProgress(l, t);
if(l >= t){
this.removeEventListener(Event.ENTER_FRAME, updatePreloadStatus);
}
}
private function updateSeekStatus(e:Event) {
_controls.setSeekProgress(this.getCurrentTime(), this.getDuration());
}
private function checkPlayerReady(e:Event) {
_count++;
if(_count >= 50){
this.removeEventListener(Event.ENTER_FRAME, checkPlayerReady);
dispatchEvent(new Event(YouTubeLoaderPlus.PLAYER_READY));
}
}
private function checkVideoReady(e:Event) {
if(this.getDuration()){
this.removeEventListener(Event.ENTER_FRAME, checkVideoReady);
dispatchEvent(new Event(YouTubeLoaderPlus.VIDEO_READY));
}
}
private function playPressed(e:Event):void {
this.play();
}
private function pausePressed(e:Event):void {
this.pause();
}
override public function loadVideoById (id:String, startSeconds:Number = 0):void{
super.loadVideoById(id, startSeconds);
this.addEventListener(Event.ENTER_FRAME, checkVideoReady);
}
override public function play():void{
super.play();
this.addEventListener(Event.ENTER_FRAME, updateSeekStatus);
}
};
};
Place this code right in the first frame of the application (remove ALL prevoius code):
import classes.*;
import classes.choppingblock.video.*;
var videoHolder = new YouTubeLoaderPlus();
videoHolder.create();
addChild(videoHolder);
videoHolder.addEventListener(YouTubeLoaderPlus.PLAYER_READY, ytPlayerReady);
videoHolder.addEventListener(YouTubeLoaderPlus.VIDEO_READY, ytVideoReady);
function ytPlayerReady(e:Event):void{
videoHolder.loadVideoById("IvTT29cavKo");
}
function ytVideoReady(e:Event):void{
videoHolder.play();
}
And the final result is:
Wow, finally we are able to control the video! That's a big step forward. Well, now it's time to move on to the next part. The next part will deal mainly with the administrative part that will allow the admin to add new movies, new levels, now goofs etc.
To view the entire series please visit http://www.insideria.com/series-facebook-dev.html




Facebook Application Development
when i try to get uesr id in flash CS3, Error #1009 come out:
at main_1/::handleGetInfoResponse()
at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at com.facebook.net::FacebookCall/http://api.facebook.com/1.0/::handleResult()
at com.facebook.delegates::WebDelegate/com.facebook.delegates:WebDelegate::handleResult()
at com.facebook.delegates::WebDelegate/com.facebook.delegates:WebDelegate::onDataComplete()
at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flash.net::URLLoader/flash.net:URLLoader::onComplete()
what mean of this?
after the followin After: //load the video by id
videoHolder.loadVideoById("lPgxsGRpiuw");
I get the following error when previewing in Flash:
YouTubeLoaderEvent.as Line 1
5001: The name of package 'choppingblock.video' does not reflect the location of this file. Please change the package definition's name inside this file, or move the file. blablabla/classes/choppingblock/video/YouTubeLoaderEvent.as
Following error upon seekProgress, and setSeekProgress testing:
SeekBar.as, Line 20 1180: Call to a possibly undefined method SuperEvent.