|
Jeremy, I went ahead and put together a demo this morning of a player that uses a BEML template with a custom SWF module to retrieve and display the hits/total plays for the video. It would be great to see it up on the Developer Center! http://link.brightcove.com/services/player/bcpid25003080001?@videoPlayer=174311064 The BEML for this player is based on the BEML template for a standard single-title player with the MediaControls separated into their components. The two additions are: 1. The <Modules> tag, which provides a link to the SWF, including our account Media API Read Token attached as a parameter: <Modules> <Module file="http://diginovations.bizland.com/sandbox/projects/flex/BC3Components/HitCounter/HitCounter.swf?token=[Media API Token]"/> </Modules>
2. And a <Label> component inserted into the bottom row of the MediaControls, which serves as a placeholder hits counter: <Label id="hitCounter" width="56" height="17" vAlign="middle" hAlign="left" text="" />
The advantage here of using a <Module> and a <Label> over using a <SWFLoader> component to display the hits is that the <Label> can be easily formatted in the Brightcove Studio along with the other player components. The entire BEML template looks like this: <Runtime>
<Theme name="Deluxe" style="Light" /> <Modules> <Module file="http://diginovations.bizland.com/sandbox/projects/flex/BC3Components/HitCounter/HitCounter.swf?token=[Media API Token]"/> </Modules> <Layout boxType="vbox" padding="3"> <VideoDisplay id="videoPlayer" showBack="true" includeFullscreenControls="true"/> <MediaControls id="mediaControls" height="46"> <HBox width="460" height="12" x="10" y="5" gutter="21"> <Label width="31" height="11" vAlign="middle" hAlign="right" text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}" /> <VBox> <Spacer height="1" /> <Playhead id="playhead" mediaController="{videoPlayer}" /> </VBox> <Label width="31" height="11" vAlign="middle" hAlign="left" text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}" /> </HBox> <HBox width="465" height="19" x="5" y="25" gutter="10"> <ToggleButton id="playButton" showBack="false" iconName="play" toggledIconName="pause" label="controls play" toggledLabel="controls pause" tooltip="controls play tooltip" toggledTooltip="controls pause tooltip" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="20" click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}" toggled="{videoPlayer.playing}"/> <Spacer /> <Button id="shareButton" showBack="false" iconName="share" label="community share" tooltip="community share tooltip" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="21" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Email', data)}"/> <Button id="linkButton" showBack="false" iconName="link" label="community link" tooltip="community link tooltip" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="21" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Link', data)}"/> <Button id="codeButton" showBack="false" iconName="code" label="community code" tooltip="community code tooltip" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="21" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Embed', data)}"/> <Spacer /> <Label id="hitCounter" width="56" height="17" vAlign="middle" hAlign="left" text="" /> <Spacer /> <Button id="maximizeButton" showBack="false" iconName="maximize" tooltip="controls maximize tooltip" width="19" height="17" click="{videoPlayer.goFullScreen()}"/> <Button id="volumeButton" showBack="false" iconName="volume" tooltip="controls volume tooltip" width="19" height="17" click="{videoPlayer.toggleVolumeControls()}"/> <Button id="menuButton" showBack="false" iconName="menu" label="controls menu" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="19" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Info', data)}"/> </HBox> </MediaControls> </Layout> </Runtime>
Meanwhile, the SWF is compiled from an ActionScript project that extends the CustomModule class provided in the Brightcove Player API SWC. All of the logic is contained in a single document class, HitCounter.as, though it requires the Brightcove SWC and the AS3CoreLib SWC for JSON parsing. package { import com.adobe.serialization.json.JSON; import com.brightcove.api.APIModules; import com.brightcove.api.CustomModule; import com.brightcove.api.components.Label; import com.brightcove.api.dtos.VideoDTO; import com.brightcove.api.events.MediaEvent; import com.brightcove.api.modules.ContentModule; import com.brightcove.api.modules.ExperienceModule; import com.brightcove.api.modules.VideoPlayerModule; import flash.display.StageScaleMode; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import flash.net.URLRequestMethod; import flash.net.URLVariables; public class HitCounter extends CustomModule { private var _mediaService:String = 'http://api.brightcove.com/services/library'; private var _mediaKey:String; public function HitCounter() { } /** * Initializes the component once the Brightcove API is ready for access. */ override protected function initialize():void { getMediaKey(); updateMedia(); addListeners(); } /** * Gets the mediaKey from the token parameter in the SWF URL: * ../HitCounter.swf?token=[Media API token] */ private function getMediaKey():void { _mediaKey = root.loaderInfo.parameters['token']; } /** * Adds event listeners to the Brightcove Modules. */ private function addListeners():void { videoPlayerModule.addEventListener(MediaEvent.CHANGE, handleMediaChange); } /** * Handles any change of media in the player, which occurs when a new video loads. */ private function handleMediaChange(event:MediaEvent):void { updateMedia(); } /** * Whenever the media in the Brightcove player has changed, a new request to the Media API needs to be * made to retrieve metadata not available directly via the Player API. */ private function updateMedia():void { var video:VideoDTO = videoPlayerModule.getCurrentVideo(); if (video) { getPlaysTotal(video.id); } } /** * Generates a request to the Brightcove Media API to retrieve the playsTotal for the video. */ private function getPlaysTotal(id:Number):void { var request:URLRequest = new URLRequest(_mediaService); var loader:URLLoader = new URLLoader();
var variables:URLVariables = new URLVariables(); variables.token = _mediaKey; variables.command = 'find_video_by_id'; variables.video_id = id; variables.video_fields = 'playsTotal'; request.data = variables; request.method = URLRequestMethod.GET; loader.dataFormat = URLLoaderDataFormat.TEXT; loader.addEventListener(Event.COMPLETE, handleGetPlaysTotal); try { loader.load(request); } catch (error:ArgumentError) { trace("An ArgumentError has occurred."); } catch (error:SecurityError) { trace("A SecurityError has occurred."); } } /** * Handles the response from the Brightcove Media API for the request for the playsTotal data. */ private function handleGetPlaysTotal(event:Event):void { var loader:URLLoader = URLLoader(event.target); var response:Object = JSON.decode(loader.data); if (response.playsTotal) { updateHits(response.playsTotal); } } /** * Identifies the Label component in the Brightcove player's BEML template designated as the "hitCounter" * and updates its text to reflect the number of playsTotal retrieved from Media API. */ private function updateHits(hits:Number):void { var hitCounter:Label = experienceModule.getElementByID('hitCounter') as Label; if (hitCounter) { hitCounter.setText(hits + ' views'); } } /** * Returns a reference to the VideoPlayerModule. */ public function get videoPlayerModule():VideoPlayerModule { return player.getModule(APIModules.VIDEO_PLAYER) as VideoPlayerModule; } /** * Returns a reference to the ContentModule.. */ public function get contentModule():ContentModule { return player.getModule(APIModules.CONTENT) as ContentModule; } /** * Returns a reference to the ExperienceModule.. */ public function get experienceModule():ExperienceModule { return player.getModule(APIModules.EXPERIENCE) as ExperienceModule; }
} }
The SWF waits for the CustomModule class (its parent class) to call the initialize() function, at which point it looks for the Media API token in the parameters of the URL, updates itself for the currently loaded video, and listens for any changes to the video. Whenever a new video is loaded, the updateMedia() function uses the video ID to call the find_video_by_id command in the Media API, and specifically requests that it return only the playsTotal field. Once the service responds, the JSON utility parses out the value, and then assigns it as the text for the <Label> we created in the BEML template. This example could be extended in a variety of ways. For one, the hitCounter label could be placed anywhere in a BEML template, not just stuck in the MediaControls. You could also modify the SWF to provide other pieces of data from the Media API that aren't currently available in the Player API, such as the direct URL to the media file.
|