AS3 YouTube Chromeless player and search API

If you read my previous post on the AS3 Lyricsfly API and my last post AS3 YouTube Chromeless API you will know that I’m creating an online Video Karaoke application for fun.

FIRST and foremost I must thank Martin Legris for providing the search functionality. Martin has wrapped up the YouTube API including the parsing of its feeds. He’s even gone out of his way to develop a proxy using AMFPHP which supports updating or deleting:

* video responses
* comments
* ratings
* subscriptions
* profile information
* contacts
* video information.
and you can post ratings, comments, create subscriptions and add contacts. I believe he will be submitting the code base to Google so they can make the AS3 API official.
SO ONCE AGAIN…THANK YOU MARTIN!

As mentioned, this is the Part 2 to Part 1 of the YouTube AS3 API. The last post focused on using the chromeless player and introduced you to creating your very own video controls. This post is focused on searching YouTube for videos and displaying it using your own custom scroller.
In the example below I used the standard feeds provided by the YouTube API and the ability to search for video content on YouTube. I also created a custom scroller out of MovieClip elements that designers can re-use and re-design. I’m not going to post a tutorial on the subject. I’m just offering the basics.

Get Adobe Flash player

Let’s break it down!
The document class Main.as simply creates the view. It is similar to its predecessor but it has been revised to adapt to the search functionality. It also instantiates the new YouTubeSearch.as class and the PanelScroller.as class, which are the main focus of this posting. Most of the code is rudimentary, its simply sets up the flash components adds event listeners and implements methods that handle the changes. The new methods which set up the search form and handles the video data are createSearchForm() and createVideoScroller().

Main.as

/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package {

    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import fl.controls.TextInput;
    import fl.controls.RadioButton;
    import fl.controls.Button;
    import fl.controls.ButtonLabelPlacement;
    import fl.controls.RadioButtonGroup;
    import com.YouTube.*;
    import com.video.*;
    import com.view.PanelScroller;

    public class Main extends Sprite {
        private var youTubePlayer:YouTubePlayer;
        private var youTubeSearch:YouTubeSearch;
        private var videoPlayerContainer:MovieClip;
        private var videoPlayerControls:VideoPlayerControls;
        private var videoPlayerWidth:Number=320;
        private var videoPlayerHeight:Number=240;
        private var defaultVideoByID:String ="0UjsXo9l6I8";
        private var currentSearchKeyword:String;
        private var videoBkg:Sprite;
        private var searchButton:Button;
        private var ti1:TextInput;
        private var searchInput:TextInput;
        private var videoScroller:PanelScroller;
        private var circleAnimation:MovieClip;
        private var searchRB:RadioButton;
        private var topRB:RadioButton;
        private var featuredRB:RadioButton;
        private var mostDiscussedRB:RadioButton;
        private var standardFeedMaxResults:Number = 25;

        /*Constructor*/
        public function Main() {
            createVideoBkg();
            setupInputFields();
            videoPlayerContainer = new MovieClip();
            videoPlayerContainer.x = 25;
            videoPlayerContainer.y = 10;
            addChild(videoPlayerContainer);
            createYouTubePlayer();
            createVideoPlayerControls();
            createSearchForm();
            createVideoScroller();
            createRadioButton();
        }
        /*
        Method:createVideoScroller
        Parameters:
        Return:
        */

        private function createVideoScroller():void {
            videoScroller = new PanelScroller();
            videoScroller.visible=false;
            videoScroller.x= searchInput.x;
            videoScroller.y = (searchInput.y + searchInput.height) + 50;
            videoScroller.addEventListener(Event.CHANGE,videoSelectedFromScroller);
            addChild(videoScroller);
        }
        /*
        Method:videoSelectedFromScroller
        Parameters:
        event:Event
        Returns:
        */

        private function videoSelectedFromScroller(event:Event):void {
            var videoObject = videoScroller.currentPanel;
            defaultVideoByID = videoObject.id;
            youTubePlayer.loadVideoById(videoObject.id);
            ti1.text = "Loading Video : ID: " +  defaultVideoByID;
        }
        /*
        Method:createSearchForm
        Parameters:
        Return:
        */

        private function createSearchForm():void {
            youTubeSearch = YouTubeSearch.getInstance();
            searchInput = new TextInput();
            searchInput.move(videoBkg.x,(videoBkg.y + videoBkg.height) + 50);
            searchInput.width = 200;
            addChild(searchInput);
            searchButton = new Button();
            searchButton.x = (videoBkg.x + videoBkg.width) - searchButton .width;
            searchButton.y = (videoBkg.y + videoBkg.height)+50;
            searchButton.label = "Search";
            addChild(searchButton);
            addSearchFormListeners();
        }
        /*
        Method:removeSearchFormListeners
        Parameters:
        Returns:
        */

        private function removeSearchFormListeners():void {
            searchButton.removeEventListener(MouseEvent.CLICK, startVideoSearch);
            searchButton.enabled = false;
            searchInput.removeEventListener(Event.CHANGE,updateSearchKeywords);
            searchInput.enabled = false;
        }
        /*
        Method:addSearchFormListeners
        Parameters:
        Returns:
        */

        private function addSearchFormListeners():void {
            searchButton.addEventListener(MouseEvent.CLICK, startVideoSearch);
            searchButton.enabled = true;
            searchInput.addEventListener(Event.CHANGE,updateSearchKeywords);
            searchInput.enabled = true;
        }
        /*
        Method: updateSearchKeywords
        Parameters:
        event:Event
        Returns:
        */

        private function updateSearchKeywords(event:Event):void {
            if (searchInput.text !="") {
                currentSearchKeyword = searchInput.text;
            }
        }
        /*
        Method: addLoadingAnimation
        Parameters:
        Returns:
        */

        private function addLoadingAnimation():void {

            circleAnimation = new CircleAnimation();
            circleAnimation.x = 185 - circleAnimation.width;
            circleAnimation.y = (videoBkg.y + videoBkg.height)+250;
            addChild(circleAnimation);
        }
        /*
        Method: removeLoadingAnimation
        Parameters:
        Returns:
        */

        private function removeLoadingAnimation():void {
            removeChild(circleAnimation);
        }
        /*
        Method: startVideoSearch
        Parameters:
        event:Event
        Returns:
        */

        private function startVideoSearch(event:Event):void {

            addLoadingAnimation();
            removeSearchFormListeners();
            videoScroller.visible=false;
            youTubeSearch.startSearch(currentSearchKeyword);
            youTubeSearch.addEventListener(YouTubeSearchEvent.ON_VIDEO_DATA_READY,refreshDataScroller);
        }
        /*
        Method:refreshDataScroller
        Parameters:
        event:YouTubeSearchEvent
        Returns:
        */

        private function refreshDataScroller(event:YouTubeSearchEvent):void {
            removeLoadingAnimation();
            addSearchFormListeners();
            videoScroller.visible=true;
            var dArray:Array = event.data;
            videoScroller.dataprovider = dArray;
        }
        /*
        Method: destroy
        Parameters:
        Returns:
        */

        private function destroy():void {
            videoPlayerControls.destroy();
            youTubePlayer.destroy();
            removeChild(videoPlayerContainer);
            removeChild(videoPlayerControls);
        }
        /*
        Method: createVideoBkg
        Parameters:
        Returns:
        */

        private function createVideoBkg():void {
            videoBkg = new Sprite();
            videoBkg.x = 25;
            videoBkg.y = 10;
            createRectangle(videoBkg,0x000000,videoPlayerWidth,videoPlayerHeight);
            addChild(videoBkg);
        }
        /*
        Method: createVideoPlayerControls
        Parameters:
        Returns:
        */

        private function createVideoPlayerControls():void {
            videoPlayerControls = new VideoPlayerControls();
            videoPlayerControls.visible=false;
            videoPlayerControls.videoWidth = videoPlayerWidth;
            videoPlayerControls.videoHeight = videoPlayerHeight;
            videoPlayerControls.addEventListener(VideoPlayerControls.ON_PLAYBACK_STATE_CHANGE,playerControlsOnChange);
            videoPlayerControls.addEventListener(VideoPlayerControls.ON_SOUND_CHANGE,playerControlsSoundOnChange);
            videoPlayerControls.x = videoPlayerContainer.x;
            videoPlayerControls.y = videoPlayerContainer.y + (videoPlayerHeight + 8);
            videoPlayerControls.init();
            addChild(videoPlayerControls);

        }
        /*
        Method: createYouTubePlayer
        Parameters:
        Returns:
        */

        private function createYouTubePlayer():void {
            youTubePlayer = new YouTubePlayer();
            youTubePlayer.addEventListener(YouTubeEvent.ON_READY, playerReady);
            youTubePlayer.addEventListener(YouTubeEvent.ON_CHANGE, onPlayerStateChange);
            youTubePlayer.addEventListener(YouTubeEvent.ON_ERROR,onPlayerError);
            youTubePlayer.createPlayer(videoPlayerContainer);
        }
        /*
        Method: onPlayerError
        Parameters:
        event:YouTubeEvent
        Returns:
        */

        private function onPlayerError(event:YouTubeEvent):void {
            trace(event.data);
            var msg:String;
            switch (event.data) {
                case 100 :
                    msg = "Video was not found or is private";
                    break;
                case 101 :
                    msg = "Video not allowed playback in embedded players";
                    break;
                case 150 :
                    msg = "Video not allowed playback in embedded players";
                    break;
            }
            ti1.text =  msg;
        }
        /*
        Method:playerControlsOnChange
        Parameters:
        event:VideoControlsEvent
        Returns:
        */

        private function playerControlsOnChange(event:VideoControlsEvent):void {

            var buttonState:String = event.data.state;
            switch (buttonState) {
                case "play" :
                    youTubePlayer.playVideo();
                    break;
                case "pause" :
                    youTubePlayer.pauseVideo();
                    break;
                case "seek" :
                    youTubePlayer.seekTo(event.data.val,true);
                    break;
            }
        }
        /*
        Method:playerControlsSoundOnChange
        Parameters:
        event:VideoControlsEvent
        Returns:
        */

        private function playerControlsSoundOnChange(event:VideoControlsEvent):void {

            var buttonState:String = event.data.state;
            switch (buttonState) {
                case "mute" :
                    youTubePlayer.mute();
                    break;
                case "unmute" :
                    youTubePlayer.unMute();
                    break;
                case "adjust_volume" :
                    youTubePlayer.setVolume(event.data.val);
                    removeEventListener(Event.ENTER_FRAME,trackProgress);
                    break;
            }
        }
        /*
        Method: playerReady
        Parameters:
        event:YouTubeEvent
        Returns:
        */

        private function playerReady(event:YouTubeEvent):void {
            youTubePlayer.setSize(videoPlayerWidth,videoPlayerHeight);
            youTubePlayer.cueVideoById(defaultVideoByID);
        }
        /*
        Method: onPlayerStateChange
        Parameters:
        event:Event
        Returns:
        */

        private function onPlayerStateChange(event:Event):void {
            var _state = event.data;
            switch (_state) {
                case -1 :
                    //trace("unstarted");
                    break;
                case 0 :
                    //trace("ended");
                    videoPlayerControls.setPlayPauseControls(youTubePlayer.PlayerState);
                    removeEventListener(Event.ENTER_FRAME,trackProgress);
                    break;
                case 1 :
                    //trace("playing" + youTubePlayer.volume);
                    ti1.text = "Playing : ID : " + defaultVideoByID;
                    youTubePlayer.setVolume(50);
                    videoPlayerControls.setPlayPauseControls(youTubePlayer.PlayerState);
                    videoPlayerControls.setMuteControl(youTubePlayer.isMuted());
                    videoPlayerControls.setSeekDuration(youTubePlayer.duration);
                    addEventListener(Event.ENTER_FRAME,trackProgress);
                    break;
                case 2 :
                    //trace("paused");
                    break;
                case 3 :
                    //trace("buffering");

                    break;
                case 5 :
                    //trace("video cued");
                    videoPlayerControls.visible=true;
                    ti1.text = "Video Cued : ID : "+ defaultVideoByID;
                    break;
            }
        }
        /*
        Method:trackProgress
        Parameters:
        event:Event
        Returns:
        */

        private function trackProgress(event:Event):void {
            var _bytesLoaded = youTubePlayer.VideoBytesLoaded;
            var _bytesTotal = youTubePlayer.VideoBytesTotal;

            videoPlayerControls.setLoadingProgress({bytes:_bytesLoaded,total:_bytesTotal});
            videoPlayerControls.setSeekCurrentTime(youTubePlayer.CurrentTime);

        }
        /*
        Method: setupInputFields
        Parameters:
        Returns:
        */

        private function setupInputFields():void {
            ti1 = new TextInput();
            ti1.move(videoBkg.x,725);
            ti1.editable = false;
            ti1.width = videoBkg.width;
            ti1.text = "Video ID Info";
            addChild(ti1);
        }
        /*
        Method:updateDefaultVideoById
        Parameters:
        event:Event
        Returns:
        */

        private function updateDefaultVideoById(event:Event):void {
            defaultVideoByID = ti1.text;
        }
        /*
        Method: startStandardFeedSearch
        Parameters:
        inStr:String
        Returns:
        */

        private function startStandardFeedSearch(inStr:String):void {

            addLoadingAnimation();
            removeSearchFormListeners();
            videoScroller.visible=false;
            switch (inStr) {
                case "toprated" :
                    youTubeSearch.loadTopRatedFeed(1,standardFeedMaxResults);
                    break;
                case "featured" :
                    youTubeSearch.loadRecentlyFeaturedFeed(1,standardFeedMaxResults);
                    break;
                case "mostdiscussed" :
                    youTubeSearch.loadMostDiscussedFeed();
                    break;
            }
            youTubeSearch.addEventListener(YouTubeSearchEvent.ON_STANDARD_VIDEO_DATA_FEED_READY,standardFeedLoaded);
        }
        /*
        Method:standardFeedLoaded
        Parameters:
        event:YouTubeSearchEvent
        Returns:
        */

        private function standardFeedLoaded(event:YouTubeSearchEvent):void {
            removeLoadingAnimation();
            videoScroller.visible=true;
            var dArray:Array = event.data;
            videoScroller.dataprovider = dArray;
        }
        /*
        Method: radioButtonGroupChangeState
        Parameters:
        event:Event
        Returns:
        */

        private function radioButtonGroupChangeState(event:Event):void {
            var rbg:RadioButtonGroup = event.target as RadioButtonGroup;
            var rb:RadioButton = rbg.selection;
            var currentInstanceName:String = rb.name;
            switch (currentInstanceName) {
                case "search" :
                    addSearchFormListeners();
                    break;
                case "toprated" :
                    startStandardFeedSearch(currentInstanceName);
                    break;
                case "featured" :
                    startStandardFeedSearch(currentInstanceName);
                    break;
                case "mostdiscussed" :
                    startStandardFeedSearch(currentInstanceName);
                    break;

            }
        }
        /*
        Method: createRadioButton()
        Parameters:
        Returns:
        */

        private function createRadioButton():void {
            var rbg:RadioButtonGroup = new RadioButtonGroup("searchgroup");
            searchRB = new RadioButton();
            searchRB.name = "search";
            searchRB.group = rbg;
            searchRB.label="Search";
            searchRB.labelPlacement=ButtonLabelPlacement.BOTTOM;
            searchRB.selected = true;
            searchRB.move(0, (searchInput.y + searchInput.height) + 15);


            topRB = new RadioButton();
            topRB.name = "toprated";
            topRB.group = rbg;
            topRB.label="Top Rated";
            topRB.labelPlacement=ButtonLabelPlacement.BOTTOM;
            topRB.selected = false;
            topRB.move(70, (searchInput.y + searchInput.height) + 15);

            featuredRB = new RadioButton();
            featuredRB.name = "featured";
            featuredRB.group = rbg;
            featuredRB.label="Recently Featured";
            featuredRB.labelPlacement=ButtonLabelPlacement.BOTTOM;
            featuredRB.selected = false;
            featuredRB.move(160, (searchInput.y + searchInput.height) + 15);

            mostDiscussedRB = new RadioButton();
            mostDiscussedRB.name = "mostdiscussed";
            mostDiscussedRB.group = rbg;
            mostDiscussedRB.label="Most Discussed";
            mostDiscussedRB.labelPlacement=ButtonLabelPlacement.BOTTOM;
            mostDiscussedRB.selected = false;
            mostDiscussedRB.move(270, (searchInput.y + searchInput.height) + 15);


            addChild(searchRB);
            addChild(topRB);
            addChild(featuredRB);
            addChild(mostDiscussedRB);
            rbg.addEventListener(Event.CHANGE, radioButtonGroupChangeState);
        }
        /*
        Method:createRectangle
        Parameters:
        inSrc:*
        inColor:Number
        inW:Number;
        inH:Number;
        Return:
        */

        private function createRectangle(inSrc:*,inColor:Number=0x999999,inW:Number=80,inH:Number=50):void {
            var rect:Shape=new Shape();
            rect.graphics.clear();
            rect.graphics.beginFill(inColor);
            rect.graphics.drawRect(0,0,inW,inH);
            rect.graphics.endFill();
            inSrc.addChild(rect);
        }

    }
}

If you read the AS3 YouTube Chromeless API post, you will be familiar with the YouTubePlayer.as class and the VideoPlayerControls.as class. If you downloaded the source, I advise you to update your files, since I did some code cleanup.

The YouTubeSearch.as class is a singleton that handles the search functionality including the instantiation of Martin Legris YouTubeClient. YouTubeSearch extends EventDispatcher which gives us the ability to dispatch a custom event, passing along the search result data. The class also builds an array which contains objects for each individual video data that is passed back to the video panel scroller. You can extend the functionality by adding new methods to support your application. For example, you can add a login to your application which enables the ability to access your YouTube Profile and retrieve your list of videos, comments, ect. As I mentioned Martin’s YouTubeClient rocks, but for my application I just need the simple search functionality.

YouTubeSearch.as

/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package com.YouTube
{
    import com.newcommerce.youtube.data.ThumbnailData;
    import com.newcommerce.youtube.data.VideoData;
    import com.newcommerce.youtube.events.VideoFeedEvent;
    import com.newcommerce.youtube.events.StandardVideoFeedEvent;
    import com.newcommerce.youtube.feeds.VideoFeed;
    import com.newcommerce.youtube.iterators.ThumbnailIterator;
    import com.newcommerce.youtube.webservice.YouTubeClient;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IEventDispatcher;

    public class YouTubeSearch extends EventDispatcher
    {
        private static  var _instance:YouTubeSearch;
        protected var _ws:YouTubeClient;       
        protected var _requestId:Number;
       
       
        public function YouTubeSearch(singletonEnforcer:SingletonEnforcer) {
            var target:IEventDispatcher=null
            super(target);
        }
       
        public static function getInstance():YouTubeSearch {
            if (_instance == null) {
                _instance=new YouTubeSearch(new SingletonEnforcer  );
                _instance.init();
            }
            return _instance;
        }
        /*
        Method:init
        Parameters:
        Returns:
        */

        public function init():void{
           
            _ws = YouTubeClient.getInstance();
            _ws.addEventListener(VideoFeedEvent.VIDEO_DATA_RECEIVED, videoSearchResults);
            _ws.addEventListener(StandardVideoFeedEvent.STANDARD_VIDEO_DATA_RECEIVED, standardVideoFeedReady);
        }
        /*
        Method:startSearch
        Parameters:
        inSearchString:String
        Returns:
        */

        public function startSearch(inSearchString:String):void
        {
            _requestId = _ws.getVideos(inSearchString, "", null, null, ["music"], ["videos"], YouTubeClient.ORDER_BY_VIEWCOUNT, YouTubeClient.RACY_INCLUDE);
        }
        /*
        Method:loadTopRatedFeed
        Parameters:
        inStartIndex:Number=1
        inMaxResults:Number=10
        Returns:
        */

        public function loadTopRatedFeed(inStartIndex:Number=1, inMaxResults:Number=10):void
        {
            _requestId = _ws.getStandardFeed(YouTubeClient.STD_TOP_RATED, YouTubeClient.TIME_TODAY, inStartIndex,inMaxResults);
        }
        /*
        Method:loadRecentlyFeaturedFeed
        Parameters:
        inStartIndex:Number=1
        inMaxResults:Number=10
        Returns:
        */

        public function loadRecentlyFeaturedFeed(inStartIndex:Number=1, inMaxResults:Number=10):void
        {
           
            _requestId  = _ws.getStandardFeed(YouTubeClient.STD_RECENTLY_FEATURED, YouTubeClient.TIME_TODAY, inStartIndex,inMaxResults);
        }
        /*
        Method:loadMostDiscussedFeed
        Parameters:
        Returns:
        */

        public function loadMostDiscussedFeed():void
        {
            _requestId  = _ws.getStandardFeed(YouTubeClient.STD_MOST_DISCUSSED, YouTubeClient.TIME_TODAY);
        }
        /*
        Method:videoBuildVideoArray
        Parameters:
        inEvent:*
        Returns:Array
        */

        private function videoBuildVideoArray(inEvent:*):Array
        {
                var feed:VideoFeed = inEvent.feed;
                var video:VideoData;   
                var videoArray:Array = new Array()
           
                while(video = feed.next())
                {
               
                    var videoObject = new Object();
                    // video title
                    videoObject.title = video.title;
                    videoObject.url = video.id;
                    videoObject.id = video.actualId;
                    videoObject.count = video.viewCount;
                    /*
                    trace("Title : " + video.title)
                    trace("ID " + video.id);
                    trace("Actual ID " + video.actualId);
                    trace("View Count " + video.viewCount);
                    */

                    var tnIt:ThumbnailIterator = video.media.thumbnails;
                    var tn:ThumbnailData;
                   
                    while(tn = tnIt.next())
                    {
                        if(checkIsFirstImage(tn.url))
                        {
                            videoObject.tnURL = tn.url;
                            //trace("tn " + tn.url + " w " + tn.width + " h " + tn.height)
                        }
                    }  
                    videoArray.push(videoObject);
                }
               
                return videoArray;
        }
        /*
        Method:standardVideoFeedReady
        Parameters:
        event:StandardVideoFeedEvent
        Returns:
        */

        private function standardVideoFeedReady(event:StandardVideoFeedEvent):void
        {
            if(_requestId == event.requestId){
            var returnArray = videoBuildVideoArray(event);
               
            }else{
                trace("this call:"+event.requestId+" isn't ours. We'll wait for the next one...");
            }
            dispatchEvent(new YouTubeSearchEvent(YouTubeSearchEvent.ON_STANDARD_VIDEO_DATA_FEED_READY,returnArray));
        }
        /*
        Method:videoSearchResults
        Parameters:
        event:VideoFeedEvent
        Returns:
        */

        private function videoSearchResults(event:VideoFeedEvent):void
        {
            if(_requestId == event.requestId)
            {
            var returnArray = videoBuildVideoArray(event);
               
            }else{
                trace("this call:"+event.requestId+" isn't ours. We'll wait for the next one...");
            }
            dispatchEvent(new YouTubeSearchEvent(YouTubeSearchEvent.ON_VIDEO_DATA_READY,returnArray));
        }
       
        /*
        Method:checkIsFirstImage
        Parameters:
        inUrl:String
        Returns: Boolean
        */

        private function checkIsFirstImage(inUrl:String):Boolean {
            var isFirstImage:Boolean;
           
            var vIndex:Number = inUrl.lastIndexOf("/");
           
            if (vIndex == -1) {
                //no last index
                isFirstImage = false;
            } else {
                if(inUrl.substr(vIndex + 1,inUrl.length) == "1.jpg"){
                    isFirstImage = true;
                }else{
                    isFirstImage = false;
                }
               
            }
           
            return isFirstImage;
        }
       
    }  
}
internal class SingletonEnforcer {
}

The PanelScroller .as class handles the visual display for the video data. The class instantiates a slider control and builds the individual panels for each search result. Up until now, any visual asset we loaded from Google has been handled by the AS3 chromeless player.
Since we want to see individual thumbnails to represent the videos, we need to load a crossdomain.xml file: Security.loadPolicyFile(“http://i.ytimg.com/crossdomain.xml”).
With out this line of code, you would never see your thumbnails in the scroller. I’m not going to cover Flash and its Secuirty issues but you can read up on it here.

PanelScroller.as

/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package com.view{
    import flash.display.Sprite;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.geom.Rectangle;
    import flash.system.*;

    public class PanelScroller extends Sprite {

        private var slider:Slider;
        private var dProvider:Array;
        private var tnArray:Array;
        private var panelsCreated:Boolean = false;
        private var panelHeight:Number;
        private var content:Sprite;
        private var contentHeight:Number = 0;
        private var contentMask:MovieClip;
        private var contentRectangle:Rectangle= new Rectangle( 0, 0, 310, 350 );
        private var selectedPanel:Object;
       
       
        public function get currentPanel():Object
        {
            return selectedPanel;
        }
        public function set dataprovider(inData:Array):void {
            dProvider = inData;
            buildContainer();
        }
        public function get dataprovider():Array {
            return dProvider;
        }
        /*Constructor*/
        public function PanelScroller() {
            Security.allowInsecureDomain("*");
            Security.allowDomain("*");
            Security.loadPolicyFile("http://i.ytimg.com/crossdomain.xml");
            slider = sliderControl;
            slider.visible = false;
            slider.addEventListener(Event.CHANGE,slideUpdate);
            content = new Sprite();
            content.x = 0;
            addChild(content);
        }
        /*
        Method:buildContainer
        Parameters:
        Returns:
        */

        private function buildContainer():void {
            if(panelsCreated)
            {
                var child:int = content.numChildren;
                while( child -- )
                {
                    content.removeChildAt(child);
                }
                slider.percent = 0;
                contentHeight = 0;
            }
           
            tnArray = new Array();
            for (var i:uint = 0; i < dProvider.length; i++) {
                tn = new VideoPanel();
                tn.control = this;
                tn.vidTitle = dProvider[i].title;
                tn.id = dProvider[i].id;
                tn.url =dProvider[i].url;
                tn.viewCount = dProvider[i].count;
                tn.tnUrl = dProvider[i].tnURL;
                content.addChild(tn);
                tnArray.push(tn);
            }
            panelsCreated = true;
            adjustLayout();
        }
        /*
        Method:adjustLayout
        Parameters:
        Returns:
        */

        private function adjustLayout():void {
            var tnHeight:Number
            for (var j:uint = 0; j < tnArray.length; j++) {
                tnArray[j].y = (tnArray[j].y+ tnArray[j].height)*j;
                tnHeight = tnArray[j].height;//I'm doing this because I didn't want to hard code the panel height
                tnArray[j].init();
            }
           
            contentHeight = tnHeight*tnArray.length;
            content.scrollRect = contentRectangle;
            if (contentHeight > slider.trackHeight) {
                slider.visible = true;
            }
        }
        /*
        Method:slideUpdate
        Parameters:
        event:Event
        Returns:
        */

        private function slideUpdate(event:Event):void {
            var pcent:Number = slider.percent;
            var scrollable:Number = contentHeight - content.scrollRect.height;
            var sr:Rectangle = content.scrollRect.clone();
            sr.y = scrollable * pcent;
            content.scrollRect = sr;
        }
        /*
        Method:setSelectedPanel
        Parameters:
        inObj:Object
        Returns:
        */

        public function setSelectedPanel(inObj:Object):void
        {
            selectedPanel = inObj;
            dispatchEvent(new Event(Event.CHANGE));
        }



    }
}

And last but not least the VideoPanel.as class. The VideoPanel class is a MovieClip which displays and loads the video data. It also has a few utility methods to format the text data. There is not much to it.

VideoPanel.as

/****************************
* Manuel Gonzalez           *
* design@stheory.com        *
* www.stheory.com           *
* www.codingcolor.com       *
*****************************/

package com.view{
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.display.Loader;
    import flash.display.Bitmap;
    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.events.IOErrorEvent;
    import flash.net.URLRequest;
    import flash.text.TextField;
    import flash.system.Security;
    import flash.events.SecurityErrorEvent;


    public class VideoPanel extends MovieClip {

        private var videoTitle:String;
        private var videoId:String;
        private var videoUrl:String;
        private var videoViewCount:String;
        private var tnPath:String;
        private var controller:Object;
        private var imgLoader:Loader;
        private var preloadBar:MovieClip;
        private var container:Sprite;
        private var contMask:Sprite;
        private var titleMaxChar:Number=35;
        private var statusTextField:TextField;

        public function set control(inObj:Object):void {
            controller = inObj;
        }
        public function get control():Object {
            return contoller;
        }
        public function set id(inStr:String):void {
            videoId = inStr;
        }
        public function get id():String {
            return videoId;
        }
        public function set url(inStr:String):void {
            videoUrl = inStr;
        }
        public function get url():String {
            return videoUrl;
        }
        public function set vidTitle(inStr:String):void {
            videoTitle = inStr.toLowerCase();
        }
        public function get vidTitle():String {
            return videoTitle;
        }
        public function set viewCount(inStr:String):void {
            videoViewCount = inStr;
        }
        public function get viewCount():String {
            return videoViewCount;
        }
        public function set tnUrl(inStr:String):void {
            tnPath = inStr;
        }
        public function get tnUrl():String {
            return tnPath;
        }
        /*Constructor*/
        public function VideoPanel() {
            super();
            preloadBar = preloader.bar;
            preloadBar.scaleX = 0;
            preloader.alpha = 0;
        }
        /*
        Method:init
        Parameters:
        Returns:
        */

        public function init():void {
            container = new Sprite();
            container.x = 10;
            container.y = 10 ;
            contMask = new Sprite();
            contMask.x = container.x;
            contMask.y = container.y;
            createRectangle(contMask,0x000000,120,90);
            addChild(contMask);
            addChild(container);
            container.mask = contMask;
            this.buttonMode = true;
            this.addEventListener(MouseEvent.CLICK,onClick);
            displayTextData();
            loadTn();
        }
        /*
        Method:displayTextData
        Parameters:
        Returns:
        */

        private function displayTextData():void {
            titleF.text = truncate(videoTitle,titleMaxChar);
            viewF.text = "views : " + formatNumberWithCommas(videoViewCount);
        }
        /*
        Method:loadTn
        Parameters:
        Returns:
        */

        private function loadTn():void {
            preloader.alpha = 1;
            imgLoader = new Loader();
            imgLoader.contentLoaderInfo.addEventListener(Event.INIT,initImage);
            imgLoader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,onProgress);
            imgLoader.contentLoaderInfo.addEventListener(Event.COMPLETE,onImgLoaded);
            imgLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            imgLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);

            imgLoader.load(new URLRequest(tnPath));
        }
        /*
        Method:securityErrorHandler
        Parameters:
        event:Event
        Returns:
        */

        private function securityErrorHandler(event:SecurityErrorEvent):void {
            statusTextField = new TextField();
            statusTextField.x = preloader.x + preloader.width/2;
            statusTextField.y = (preloader.y + preloader.height) + 2;
            statusTextField.text ="security error"+ event;
            addChild(statusTextField);
        }
        /*
        Method:ioErrorHandler
        Parameters:
        event:Event
        Returns:
        */

        private function ioErrorHandler(event:Event):void {
            statusTextField = new TextField();
            statusTextField.x = preloader.x + preloader.width/2;
            statusTextField.y = (preloader.y + preloader.height) + 2;
            statusTextField.text ="loading error"+ event;
            addChild(statusTextField);
        }
        /*
        Method:initImage
        Parameters:
        event:Event
        Returns:
        */

        private function initImage(event:Event):void {
            var image : Bitmap = imgLoader.contentLoaderInfo.content as Bitmap;
            container.addChild(image);
        }
        /*
        Method:onImgLoaded
        Parameters:
        event:Event
        Returns:
        */

        private function onImgLoaded(event:Event):void {
            preloader.alpha = 0;
            imgLoader.contentLoaderInfo.removeEventListener(Event.INIT,initImage);
            imgLoader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS,onProgress);
            imgLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE,onImgLoaded);
            imgLoader = null;
        }
        /*
        Method:onProgress
        Parameters:
        event:ProgressEvent
        Returns:
        */

        private function onProgress(event:ProgressEvent):void {
            var loaded:Number = event.bytesLoaded;
            var total:Number = event.bytesTotal;
            var percLoaded:Number = loaded/total;
            preloadBar.scaleX = percLoaded;
            //trace("onProgress : " + percLoaded)
        }
        /*
        Method:onClick
        Parameters:
        event:MouseEvent
        Returns:
        */

        private function onClick(event:MouseEvent):void {
            controller.setSelectedPanel(this);
        }
        /*
        Method:createRectangle
        Parameters:
        inSrc:*
        inColor:Number
        inW:Number;
        inH:Number;
        Return:
        */

        private function createRectangle(inSrc:*,inColor:Number=0x999999,inW:Number=80,inH:Number=50):void {
            var rect:Shape=new Shape();
            rect.graphics.clear();
            rect.graphics.beginFill(inColor);
            rect.graphics.drawRect(0,0,inW,inH);
            rect.graphics.endFill();
            inSrc.addChild(rect);
        }
        /*
        Method:truncate
        Parameters:
        p_string:String
        p_len:uint
        p_suffix:String
        Returns:
        */

        private function truncate(p_string:String, p_len:uint, p_suffix:String = "..."):String {
            if (p_string == null) {
                return '';
            }
            p_len -= p_suffix.length;
            var trunc:String = p_string;
            if (trunc.length > p_len) {
                trunc = trunc.substr(0, p_len);
                if (/[^\s]/.test(p_string.charAt(p_len))) {
                    trunc = trimRight(trunc.replace(/\w+$|\s+$/, ''));
                }
                trunc += p_suffix;
            }

            return trunc;
        }
        /*
        Method:trimRight
        Parameters:
        p_string:String
        Returns:
        */

        private function trimRight(p_string:String):String {
            if (p_string == null) {
                return '';
            }
            return p_string.replace(/\s+$/,'');
        }
        /*
        Method:formatNumberWithCommas
        Parameters:
        Returns:
        */

        private function formatNumberWithCommas(number:Number):String {

            var negNum:String = "";
            if (number<0) {
                negNum = "-";
                number = Math.abs(number);
            }
            var num:String = String(number);
            var results:Array = num.split(/\./);
            num=results[0];

            if (num.length>3) {
                var mod:Number = num.length%3;
                var output:String = num.substr(0, mod);
                for (var i:Number = mod; i<num.length; i += 3) {
                    output += ((mod == 0 && i == 0) ? "" : ",")+num.substr(i, 3);
                }
                if (results.length>1) {
                    if (results[1].length == 1) {
                        return negNum + output + "." + results[1] + "0";

                    } else {
                        return negNum + output + "." + results[1];
                    }
                } else {
                    return negNum + output;
                }
            }

            if (results.length>1) {
                if (results[1].length == 1) {
                    return negNum + num + "." + results[1] + "0";

                } else {
                    return negNum + num + "." + results[1];
                }
            } else {
                return negNum + num;
            }
        }
    }
}

Well that concludes my brief overview of the search application. I didn’t cover the Slider.as class becuase its pretty straight forward, download the source and crack it open. I included Martin’s YouTubeClient with my source but as always check for new releases here. If you have any question or improvements, leave me some comments.
Enjoy!

Download Source:
AS3 YouTube Chromeless player & Search API (85)

Share:
  • Facebook
  • Google Bookmarks
  • Faves
  • Twitter
  • Yahoo! Bookmarks

5 Responses to “AS3 YouTube Chromeless player and search API”

  1. Hans Wurst says:

    great work, thanks for sharing!

  2. siva says:

    Good Work. Is it Possible to display related videos in AS3 Youtube Chromeless player?

  3. bijoy says:

    great job!

    How to get the title of the video by passing the video id.

  4. admin says:

    @bijoy. Did you load the video feed? Was there a feed object which was returned? What exactly are you trying to do? This would help me…help you.
    M

  5. john says:

    OMG!!! THANK YOU!!! YOUR TUTORIAL ARE AWESOME…….

Leave a Reply

Coding Color | 2009 All Rights Reserved.