Code Along with Cam : Next Gen Embed Video in Facebook News Feed

I've been reading a great post by fellow Brightcove Blogger Chris Little over at our Ecosystem blogroll about how Brightcove Players have been whitelisted by Facebook. 

This essentially means that a user can share a video from your player/site and have it appear in their FB Newsfeed for immediate inline playback.

I have need of such an integration to help spread the word of a live event we're looking at doing with Drupal tomorrow.

After digging into the code - I found it had some limitations given the FB's architecture requires the metadata of the video and player to be hosted on a HTML page.   If you're doing contextual publishing then this can be relatively straightfoward.

But I'm more interested in build this out in a BEML component that can be transported to any of my player templates.

To do this I needed 3 things

1. A server side script to create the meta tags on the fly

2. A BEML component to call the FB Sharer URL

3. A BEML Image link added to the player to allow a user to action it

Server Side Script

The FB Sharer API requires the metadata of the rich media to be exposed to the FB platform via HTML meta tags.   Chris' code does this and further docs are available here.  

In a nut shell however we need our page to expose

  • title - the video title
  • description - the video description
  • image_src - a URL to an image to display in the user's newsfeed
  • video_src - the URL to our player to load
  • video_height and video_width - to set the playback size

Luckily most of the data we need is exposed via the Media API and the rest can be hard coded.

Now I'm no PHP coder, at all, but I can hack together a script when needed.  Luckily though the Echove PHP SDK is available and took all the guess work out of it.  So I put together the following page (I've removed my read token in case someone gets overly ambitious and runs up my tab on my behalf):

<?php
       
        # Include the Echove SDK
        require('echove/RC_1_0_0/echove.php');
       
        # Instantiate the class, passing it our Brightcove API tokens
        $bc = new Echove(
                'INSERT_READ_TOKEN_HERE'
        );

        # Make our API call
        $video = $bc->find('find_video_by_id', $_GET["vidid"]);               
?>
<head>
<meta name="title" content="<?php echo $video->name ?>" />
<meta name="description" content="<?php echo $video->shortDescription ?>" />
      <!-- Start Brightcove Embed for Facebook News Feed -->
      <meta name="medium" content="video" />

      <link rel="image_src" href="<?php echo $video->thumbnailURL ?>" />
      <link rel="video_src" href="http://c.brightcove.com/services/viewer/federated_f9/30183089001?isVid=1&isUI=1&publisherID=<?php echo $_GET["pubid"] ?>&playerID=<?php echo $_GET["playid"] ?>&domain=embed&videoId=<?php echo $video->id ?>" />
      <meta name="video_height" content="336" />
      <meta name="video_width" content="385" />
      <meta name="video_type" content="application/x-shockwave-flash" />
      <!-- End Brightcove Embed for Facebook News Feed -->
      <!-- facebook button end -->
</head>
</html>

And that's it really - the script will generate a page on the fly that meets the requirements of the FB Sharer API.   As you can see this script requires the video ID (vidid), publisher ID (pubid) and the player ID (playid) to be passed through at runtime.   The playid is the id of the Player to be shown on FB and not of the calling player.

So now on to the BEML template....

Creating the BEML Component

I'm not going to get into the science behind create a custom BEML Component.  You can read all about it on our Developer Support site.   At the end though all I need is a custom component (module will do) that will

  • pull metadata from the Player API
  • create the relavant Facebook Share URL call

Luckily for us FB keeps it relatively simple to call this URL http://www.facebook.com/sharer.php - all it needs is a param u which points to our server side page.  It will use this URL to gather the metadata it needs to complete the share.

Using the SWC file I created a very simple component called fbShare - here is the fbShare.as code in full

package {

    import com.brightcove.api.APIModules;
    import com.brightcove.api.CustomModule;
    import com.brightcove.api.events.ExperienceEvent;
    import com.brightcove.api.events.BEMLClickEvent
    import com.brightcove.api.modules.ExperienceModule;
    import com.brightcove.api.modules.VideoPlayerModule;
    import com.brightcove.api.components.Image;
    import com.brightcove.api.dtos.MediaDTO;
       import flash.external.*;
    import flash.net.*;

    import flash.display.Stage;
    import flash.system.Security;
   
    public class fbShare extends CustomModule {

        // ----------------------------------------------------------------------------
        // Class Vars
         private var _bcExperience:ExperienceModule;
        private var _bcVideoPlayer:VideoPlayerModule;
        private var _bcStage:Stage;
       
        private var _fbShareBtn:Image;
       
       
        public function fbShare() {
            Security.allowDomain("*");
        }
       
        /*
        * The player is ready for interaction. Checks for access to stage.
        */
        override protected function initialize():void {
            _bcExperience = player.getModule(APIModules.EXPERIENCE) as ExperienceModule;
            _bcStage = _bcExperience.getStage();
            if (_bcStage == null) {
                _bcExperience.addEventListener(ExperienceEvent.ADDED_TO_STAGE, onAddedToStage);
            } else {
                registerEvents();
            }
         }
         
        /**
        * Handler for when the player has access to the stage.
        *
        * @param event Event dispatched by ExperienceModule.
        */
        private function onAddedToStage(event:ExperienceEvent):void {
            _bcExperience.removeEventListener(ExperienceEvent.ADDED_TO_STAGE, onAddedToStage);
            registerEvents();
        }
       
         /**
        *  Register for all interesting events here.
        */
        private function registerEvents():void {
           // log("register");
           _bcStage = _bcExperience.getStage();

          
            _bcExperience = player.getModule(APIModules.EXPERIENCE) as ExperienceModule;
            if(_bcExperience != null) {
                trace("BC_TEST module Experience creation - SUCCESS");
                _fbShareBtn = _bcExperience.getElementByID('fbShareButton') as Image;
               
                if (_fbShareBtn != null) {
                    _fbShareBtn.addEventListener(BEMLClickEvent.CLICK, onBEMLClick);
                }

            }
           
            _bcVideoPlayer = player.getModule(APIModules.VIDEO_PLAYER) as VideoPlayerModule;
            if (_bcVideoPlayer != null) {
               
            }
           
        }

        // ----------------------------------------------------------------------------
        // Event Handlers
        private function onBEMLClick(event:BEMLClickEvent) {
           
            if (event.elementID == 'fbShareButton') {
                trace("BEMLClickEvent: " + event.elementID);
               
                var video:MediaDTO = _bcVideoPlayer.getCurrentVideo();
                var fbRequest:URLRequest = new URLRequest("http://www.facebook.com/sharer.php");
                var variables:URLVariables = new URLVariables();
               
                variables.u = "http://yourphpserver/fb_share.php?vidid=" + video.id + "&pubid=1155521997&playid=46544352001";
                fbRequest.data = variables;
                fbRequest.method= URLRequestMethod.POST;
               
                navigateToURL(fbRequest);
               
               
            }
        }
       
    }

}

Nothing overly complex here

  1. I create my component
  2. Wire in my click event from my Share Image (coming up next)
  3. And react by creating the FB Sharer URL as required, adding the u param (by user URLVariables I don't have to worry about encoding the server page URL with all the query params of its own)

Finally bring it together via a BEML template

Creating the Image button

I did a bit of screen capture and Photoshop job to make me a nice PNG of a FB Share image.  Then using our MediaControls and an Image I wired it up - I used a simple Video Player template derivative but feel free to use any of your templates (with Ad Mode for MediaControls now supported you don't have to worry about losing your ad integration work).

I gave my Image the ID fbShareButton which corresponds to my Component's code and the wiring is complete.

Here is my BEML code for my Seed Player.   Remember you'll need 2 players, the other will be the player that is loaded in FB (I used my Blog player which is already wired up to GoogleAnalytics but you can use your viral player or create a new one.  Stick with a Single Video Player template to keep it simple).

I also referenced my component via the Plugins Setting for the Players - it would probably be easier to reference it as a module here in the template to be inherited by all players derived from it but this was a once off for me and kept it simple.

<Runtime>
  <Theme name="Deluxe" style="Light"/>
  <Layout width="300" height="308">
    <VBox padding="0" hAlign="center">
      <Image id="logo" depth="2" height="37" hAlign="left" vAlign="top" width="300" source=""/>
      <VideoDisplay id="videoPlayer" depth="1" showBack="true" includeFullscreenControls="true"/>
      <MediaControls id="mediaControls" height="46" width="300">
        <HBox width="290" height="12" x="10" y="5" gutter="21">
          <Canvas width="31">
            <Label x="0" y="0" id="mediaPostion" width="31" height="11" vAlign="middle" hAlign="right" text="{format(videoPlayer.mediaPosition, SecondsTimecodeFormatter)}"/>
            <Label x="0" y="0" id="adMessage" height="16" width="100" vAlign="middle" hAlign="left" text="Sponorship Message" visible="false"/>
          </Canvas>
          <VBox>
            <Spacer height="1"/>
            <Playhead id="playhead" mediaController="{videoPlayer}"/>
          </VBox>
          <Canvas width="31">
            <Label id="mediaDuration" width="31" height="11" vAlign="middle" hAlign="left" text="{format(videoPlayer.mediaDuration, SecondsTimecodeFormatter)}"/>
            <Label id="adDuration" visible="false" width="31" height="11" vAlign="middle" hAlign="left" text=""/>
          </Canvas>
        </HBox>
        <HBox width="290" 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" width="100" height="19" autoSize="false" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="20" toggled="{videoPlayer.playing}" click="{videoPlayer.play()}" toggledClick="{videoPlayer.pause()}"/>
          <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" label="controls menu" height="19" autoSize="true" lockHeight="true" iconAlignmentH="left" labelAlignmentH="left" labelOffsetX="19" data="{videoPlayer.video}" click="{videoPlayer.toggleMenuPage('Info', data)}"/>
          <Image id="fbShareButton" source="http://yourserverhere/fbShare.png" tooltip="View Transcript" width="50" height="19"/>
        </HBox>
      </MediaControls>
    </VBox>
  </Layout>
</Runtime>

And that's it!

The Finished Product

Here is the finished product to take for a test spin

Some notes:

  1. At the time of writing you may NOT advertise against your content that is shown in user's newsfeed - although its my understanding that is doesn't limit you from using other interaction components in your player - like a poll or something - please do your research of the FB Policies before going forward with this.
  2. This code is supplied WITHOUT WARRANTY or SUPPORT and is for DEMO purposes only.  If you update it please post a comment linking to your implementation - it would be great to see the uptake.
  3. Plenty of improvements can be made here :) - please excuse the rough and ready coding.

Enjoy