This article will detail how to develop and plug in a search widget into a Brightcove Player using BEML. I’ve created the search widget in Flex, since the markup is very similar to BEML. The article and sample code have been updated to show filtering of the search results based on a playlist selection.

Preparation

Before creating your own widgets to plug into a Brightcove Player using BEML, make sure you read the introduction help pages to ensure you are clear on the required preparation steps. We will be using the Media API, so that you can search for videos throughout your entire account. Also make sure you have a read token handy.

Creating The Pieces

There are three main pieces to create for a search widget: the repeating display logic for the results, the widget that needs to receive a reference from the Brightcove Player it’s been plugged into, and the BEML template that actually plugs in the widget.

The Result List Component

To display the results, we’ll use a simple repeater in Flex, like this:

 
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" horizontalScrollPolicy="off" xmlns:ns1="*">
	<mx:Script>
		<![CDATA[
		import mx.formatters.NumberBase;
 
		import mx.collections.ArrayCollection;
		import mx.rpc.events.ResultEvent;
		import com.adobe.serialization.json.JSON;
		import mx.controls.Alert;
		import mx.managers.CursorManager;
		import mx.core.Application;
 
		public function SetDataProvider( dp:Object ):void
		{
			tileRepeater.dataProvider = dp;
		}
 
		private function SelectVideo(video:Object):void 
		{
			//Alert.show( "Video ID Selected: " + video.id );
			Application.application.selectVideo(video);
		}
 
 
		]]>
	</mx:Script>
	<mx:VBox x="0" y="0" width="100%" height="100%" horizontalScrollPolicy="off" borderStyle="none" cornerRadius="8">	
		<mx:Repeater id="tileRepeater">
			<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="96" horizontalScrollPolicy="off" borderStyle="none" cornerRadius="20">
				<mx:Label text="{tileRepeater.currentItem.id}" id="videoID" visible="false"/>
				<mx:HBox x="0" y="0" width="100%" height="100%" cornerRadius="8" borderStyle="solid" horizontalGap="4" paddingLeft="4" paddingTop="2">
					<mx:Image source="{tileRepeater.currentItem.thumbnailURL}" width="120" height="90" useHandCursor="true" click="{SelectVideo(event.currentTarget.getRepeaterItem())}">
						<mx:rollOverEffect>
							<mx:Glow color="#0000ff" blurXTo="5" blurYTo="5" />
						</mx:rollOverEffect> 
					</mx:Image>
					<mx:VBox>
						<mx:LinkButton label="{tileRepeater.currentItem.name}" fontWeight="bold" fontSize="10" click="{SelectVideo(event.currentTarget.getRepeaterItem())}" width="120" textAlign="left"/>
							<mx:Text id="Description" text="{tileRepeater.currentItem.shortDescription}" width="120" height="62"/>
 
					</mx:VBox>					
				</mx:HBox>
			</mx:Canvas>					
		</mx:Repeater>		
	</mx:VBox>
 
</mx:Canvas>

As for the widget itself, here is the complete code listing (using the list repeater we just defined above). I’ve added functions and handlers to loop the results from the search function to be correlated with a playlist id, selected from a combobox, as well as the initialization code to populate the combobox.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" width="300" layout="absolute" xmlns:ns1="*" initialize="InitApp()" backgroundColor="#CCCCCC" backgroundGradientAlphas="[1.0, 1.0]" backgroundGradientColors="[#FFFFFF, #CCCCCC]">
 
<mx:Script>
	<![CDATA[
		import mx.formatters.NumberBase;
		import mx.managers.CursorManager;
		import mx.collections.ArrayCollection;
		import mx.rpc.events.ResultEvent;
		import com.adobe.serialization.json.JSON;
		import mx.controls.Alert;
 
		//Replace with your own read token
		private var ReadToken: String = "Lkn9dGUsCgNfRD8cKipEmuLcr-Oo3u8wV7hdHV945Fw.";
 
		//Brightcove Service URL 
		private var ServiceURL: String = "http://api.brightcove.com/services/library";
 
		//Placeholder for BC Player Reference		
		private var BCPlayer: Object = null;
 
		//Placeholder for the result dataprovider
		private var Results: ArrayCollection;
 
		//Initialize App
		private function InitApp():void
		{
			//Initialize by getting all playlists in the account
			GetPlayLists();	
		}
 
		//BEML Widget function to receive reference to BC Experience
 
		public function  setInterface(player:Object):void 
		{
			BCPlayer = player;						
		}
 
		public function selectVideo(video:Object):void
		{
			if (BCPlayer != null) 
			{
				var Content:Object = BCPlayer.getModule("content");
				var Player:Object = BCPlayer.getModule("videoPlayer");
				//Content.getVideoAsynch(video.id);
				Player.loadVideo(video.id );			 	
			}
			else
			{
				Alert.show("You have selected video " + video.id + ", but you are not running this widget from within a Brightcove Player" );
			}
		}
 
		private function GetPlayLists():void
		{
   	     	var request:URLRequest = new URLRequest(ServiceURL);
	       	var loader:URLLoader = new URLLoader();
	      	loader.dataFormat = URLLoaderDataFormat.TEXT;
	      	request.method = URLRequestMethod.GET;
 
	      	//Set Busy Cursor
	      	CursorManager.setBusyCursor();
 
	      	//Define the REST API variables to be passed
	      	var variables:URLVariables = new URLVariables();	      	      
	      	variables.token = ReadToken;     	
	    	variables.command = "find_all_playlists";
	    	variables.fields = "id,name";
 
	      	// assign the data to be sent by GET
	      	request.data = variables;
 
	      	// add event listener
	      	loader.addEventListener(Event.COMPLETE, handlePlayListsResults);
	      	loader.load(request);			
		}
 
		private function handlePlayListsResults(evt:Event):void
		{
			//Remove Busy Cursor
			CursorManager.removeBusyCursor();	
 
			//Get the returned JSON data string
			var response:String = evt.target.data as String; 
 
			//Uncomment this if you want to see the raw response string
			Alert.show(response);
 
			//The list of returned videos is embedded in the "items" property
			//of the root JSON object, so we will decode to a container object
			var container:Object = (JSON.decode(response) as Object);
 
			//create a new ArrayCollection passing the de-serialized Array
			//ArrayCollections work better as DataProviders, as they can
			//be watched for changes.
			var dp:ArrayCollection = new ArrayCollection(container.items);
 
			//Add an "all playlist" option to the dataprovider
			dp.addItemAt( { "id" : 0, "name" : "Search All Playlists" }, 0 ); 						
 
			//pass the ArrayCollection to the VideoList as its dataProvider.
			PlayLists.dataProvider = dp;
			PlayLists.labelField = "name";
		}			
 
		private function ButtonCallAPIClick(): void
		{
   	     	var request:URLRequest = new URLRequest(ServiceURL);
	       	var loader:URLLoader = new URLLoader();
	      	loader.dataFormat = URLLoaderDataFormat.TEXT;
	      	request.method = URLRequestMethod.GET;
 
	      	//Set Busy Cursor
	      	CursorManager.setBusyCursor();
 
	      	//Define the REST API variables to be passed
	      	var variables:URLVariables = new URLVariables();
 
	      	// First, Add your read token and the search text - if any	      	
	      	variables.token = ReadToken;
 
	      	if( SearchTerm.text != "" )
	      	{
	      		variables.text = SearchTerm.text; 
		      	variables.command = "find_videos_by_text";
		    }
		    else
		    {
		    	variables.command = "find_all_videos";
		    }  
 
	      	// assign the data to be sent by GET
	      	request.data = variables;
 
	      	// add event listener
	      	loader.addEventListener(Event.COMPLETE, handleResults);
	      	loader.load(request);
 
		}
 
		private function handleResults(evt:Event):void
		{
			//Remove Busy Cursor
			CursorManager.removeBusyCursor();	
 
			//Get the returned JSON data string
			var response:String = evt.target.data as String; 
 
			//Uncomment this if you want to see the raw response string
			//Alert.show(response);
 
			//The list of returned videos is embedded in the "items" property
			//of the root JSON object, so we will decode to a container object
			var container:Object = (JSON.decode(response) as Object);
 
			//create a new ArrayCollection passing the de-serialized Array
			//ArrayCollections work better as DataProviders, as they can
			//be watched for changes.
			var dp:ArrayCollection = new ArrayCollection(container.items);
 
			//Convert the UNIX date into an AS3 Date 
			for(var i:int = 0; i< dp.length; i++)
			{
					var video:Object = dp.getItemAt(i);
					var n:Number = video.publishedDate;				 
					video.publishedDate = new Date(n);
			}			
 
 
			Results = dp;
 
			//Filter the results based on the playlist selection
			//in the combobox. If All Playlists, then id = 0
 
			if ( PlayLists.selectedItem.id == 0 )
			{
 
				//All Playlists option selected, so we can just
				//pass the ArrayCollection to the VideoList as its dataProvider.
				videoList.SetDataProvider(Results);				
			}
			else
			{
				FilterResults();
			}
 
		}	
 
		private function FilterResults():void
		{
   	     	var request:URLRequest = new URLRequest(ServiceURL);
	       	var loader:URLLoader = new URLLoader();
	      	loader.dataFormat = URLLoaderDataFormat.TEXT;
	      	request.method = URLRequestMethod.GET;
 
	      	//Set Busy Cursor
	      	CursorManager.setBusyCursor();
 
	      	//Define the REST API variables to be passed
	      	var variables:URLVariables = new URLVariables();	      	      
	      	variables.token = ReadToken;     	
	    	variables.command = "find_playlist_by_id";
	    	variables.playlist_id = PlayLists.selectedItem.id;
	    	variables.fields = "videoIds";
 
	      	// assign the data to be sent by GET
	      	request.data = variables;
 
	      	// add event listener
	      	loader.addEventListener(Event.COMPLETE, handleFilterResults);
	      	loader.load(request);				
		}	
 
		private function handleFilterResults(evt:Event):void
		{
			//Remove Busy Cursor
			CursorManager.removeBusyCursor();	
 
			//Get the returned JSON data string
			var response:String = evt.target.data as String; 
 
			//Uncomment this if you want to see the raw response string
			Alert.show(response);
 
			//The list of returned videos is embedded in the "items" property
			//of the root JSON object, so we will decode to a container object
			var container:Object = (JSON.decode(response) as Object);
 
			//create a new ArrayCollection passing the de-serialized Array
			//ArrayCollections work better as DataProviders, as they can
			//be watched for changes.
			var dp:ArrayCollection = new ArrayCollection(container.videoIds);
			var found:Boolean = false;
 
			for( var i:int = Results.length -1; i >= 0; i-- ) 
			{
				var video: Object = Results.getItemAt(i);
				found = false;
				for( var j:int = 0; j < dp.length; j++ )
				{
					var plitem: Object = dp.getItemAt(j);
 
					if( video.id == plitem )
					{
						found = true;
					}
				}
				if( ! found )
				{
					Results.removeItemAt(i);
				}
			}
			videoList.SetDataProvider(Results);								
		}			
 
	]]>
</mx:Script>
	<mx:VBox left="10" top="10" right="10" bottom="10">
 
		<mx:HBox> 
			<mx:TextInput id="SearchTerm" width="196" text="warcraft"/>
			<mx:Button label="Search" id="ButtonCallAPI" click="ButtonCallAPIClick();" width="73"/>
		</mx:HBox>
		<mx:HBox> 
			<mx:ComboBox width="274" id="PlayLists" ></mx:ComboBox>
		</mx:HBox>
 
		<ns1:VideoList id="videoList">
		</ns1:VideoList>		
	</mx:VBox>
 
</mx:Application>

To plug this widget into a BEML template, first make sure you have a cross-domain policy file in place in the root folder where you will place the compiled swf online, for example like this:

<cross-domain-policy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="http://www.adobe.com/xml/schemas/PolicyFile.xsd">
  <allow-access-from domain="*"/>
</cross-domain-policy>

Then, create a new BEML template with the following markup:

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout>
    <VBox padding="3">
      <HBox padding="3">
        <VideoPlayer id="videoPlayer"/>
        <SWFLoader width="300" height="412" source="http://active6.com/blog/code/beml_search/Brightcove_BEML_SearchWidget.swf" depth="2"/>
      </HBox>
    </VBox>
  </Layout>
</Runtime>

All that remains to do now is to create a player based on this template and publish it (make sure to enable the API access in the player’s configuration). Enjoy!

Leave a Reply

You must be logged in to post a comment. Login »