Creating MyTube with Flex and Turbogears, Part 2

This is part 2 of the "Creating MyTube with Flex and Turbogears" tutorial. In the first part of the tutorial, we have setup everything needed to interface between our flex UI and our TurboGears backend.

In this part, we will continue with the porting of the original tutorial. So now we will create a flex interface for playing our converting flash videos. First, create MovieItem.mxml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" height="80">
  <mx:Image source="{data.thumb}" width="{data.width/3}"
     height="{data.height/3}" rotation="5" left="10" top="0" />
   <mx:Label text="{data.title}" fontWeight="bold" top="10" left="100" fontSize="18" />
</mx:Canvas>

This is the representation of the list item that we are using to list all the movies with a thumbnail. So we need to use that in the actual interface. Go ahead and create mytube.mxml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="movieXmlData.send()">

<mx:HTTPService method="get" url="http://localhost:8080/listmovies" id="movieXmlData" result="onGetMovies( event )" />


<mx:Script>
import mx.rpc.events.ResultEvent;
import mx.controls.VideoDisplay;
import mx.controls.List;
import mx.rpc.http.HTTPService;
import mx.collections.ArrayCollection;
import mx.utils.ArrayUtil;

[Bindable]
private var movies : ArrayCollection = new ArrayCollection();

public function onGetMovies( event : ResultEvent ) : void
{
  movies = new ArrayCollection(ArrayUtil.toArray(event.result.movies.movie.source));
  movieList.selectedIndex = 0; 
}

public function onPrevious() : void
{
  /* loop through the list incase we're at the first element */
  if ( movieList.selectedIndex == 0 )
  {
    movieList.selectedIndex = movies.length - 1;
  }
  else
  {
    movieList.selectedIndex -= 1;
  }
  
  videoPlayer.source = this.movieList.selectedItem.source.toString();
}

public function onPlay() : void
{
  videoPlayer.source = this.movieList.selectedItem.source.toString();
  videoPlayer.play();
}

public function onNext() : void
{
  /* loop through the list if we reach the end */
  if ( movieList.selectedIndex >= ( movies.length - 1 ) )
  {
    movieList.selectedIndex = 0;
  }
  else
  {
    movieList.selectedIndex += 1;
  }
    
  videoPlayer.source = this.movieList.selectedItem.source.toString();
}

public function onChange() : void
{
  videoPlayer.source = this.movieList.selectedItem.source.toString();
}

</mx:Script>

<mx:HBox width="100%" paddingLeft="10" paddingTop="10" paddingRight="10">
  <mx:VBox>
    <mx:VideoDisplay width="400" height="300" id="videoPlayer" />
    <mx:HBox width="100%" horizontalAlign="center">
       <mx:Button label="Prev" click="onPrevious()" />
       <mx:Button label="Play" click="onPlay()" />
       <mx:Button label="Stop" click="videoPlayer.stop()"/>
       <mx:Button label="Next" click="onNext()" />
    </mx:HBox>
    </mx:VBox>
    <mx:List width="100%" height="340" id="movieList"
      dataProvider="{movies}"
      change="onChange()"
      itemRenderer="MovieItem"></mx:List>
</mx:HBox>

</mx:Application>

You may notice that this not exactly the same code from the original tutorial, i modified the actionscript a bit to make it better.

Next, we need to create the corresponding server side logic in the controllers.py. So add the following code to the controller.

    @expose(content_type="text/xml")
    def listmovies(self):
    	moviesxml = "<movies>"
    	
    	query = session.query(Movie)
	selectedmovies = query.select()
	
	for movie in selectedmovies:
		moviesxml += "<movie title='%s' source='showmovie?movieid=%s' thumb='showthumb?movieid=%s' width='%s' height='%s'/>"%(movie.title, str(movie.movieid), str(movie.movieid), str(movie.width), str(movie.height))
		
	moviesxml += "</movies>"
	return moviesxml

As you can see, this function will return an xml representation of our stored movies. Also notice that this xml references two addresses, the showmovie which we already defined and the showthumb which we will define now. So add the following to the controller which will serve the thumbnail pictures to our flex application.

    @expose()
    def showthumb(self, movieid = None):
    	if( movieid != None):
    		query = session.query(Movie)
		reqmovie = query.get_by(movieid=movieid)
		
		if(reqmovie != None):
			return serveFile(reqmovie.thumb)
		else:
			return dict()
	else:
		return dict()

Next, we need to create our template page that will host the flex application. So create a flexinterface.html template with the following code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">

<head>

<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>

<title>Video Sharing Site</title>

</head>

<body>
	<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="650" height="400" codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
		<param name="movie" value="mytube" />
		<param name="quality" value="high" />
		<embed src="mytube" quality="high" width="650" height="400" play="true" loop="false" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer">
		</embed>
	</object>
</body>
</html>

This will also need the required code in the controller:

    @expose(template="genshi:moviestg.templates.flexinterface")
    def flexinterface(self):
    	return dict()

What remains is to serve the mytube swf from our controller, so add the following code to the controller:

    @expose()	
    def mytube(self):
    	executionpath = os.getcwd()
 	return serveFile(executionpath + "/mytube.swf")

After this, all that remains is to visit http://localhost:8080/flexinterface to see the magic.