Getting Started with Brightcove and the Mojo SDK for WebOS

Technology
Media API
Required
Media API token
Edition
Express 499, Pro, Enterprise

A look at developing an application for Palm's WebOS that leverages Brightcove's Media API to access videos in a publisher's account.

With the recent launch of the Palm Pre, Palm also released a new operating system, WebOS, a Linux-based operating system that serves as the platform for the Pre, the upcoming Pixi, and other planned or rumored phones in the future. Developers can write web applications for WebOS, just as in the early days of iPhone development. However, for a richer, more usable application, Palm provides the Mojo SDK. Let's take a look at developing an application for WebOS that leverages Brightcove's Media API to access videos in a publisher's account.

Mojo SDK

The Mojo SDK is based on a combination of JavaScript and HTML. Mojo extends the set of standard HTML controls and styles. These extended controls are called widgets and include such things as: lists, alert dialogs, drawers, menus, date and number pickers, progress bars, spinners, etc. Examples of all of these widgets are available in the sample apps included with the Mojo SDK.

To get started, you'll need to register for an account at http://developer.palm.com. Registration is free and provides access to the SDK, documentation, online forums and more. Mojo is available for Linux, OS X, and Windows. Follow the step-by-step instructions for installing the Mojo SDK; there are some dependencies that must be in place before installing Mojo.

Creating an application

If you're familiar with Ruby on Rails or similar frameworks, the next step will look a little familiar. The first step in creating a new WebOS app is running palm-generate. This creates the skeleton of a new application with all the files required for a WebOS application.

Mojo implements its UI with the metaphor of a stage and scenes. A scene is each distinct UI view, whether it's a splash screen, a Preferences page, or a list of videos. The stage manages the scenes, ensuring that only one scene is active at a time. The stage also manages a scene stack, allowing scenes to be pushed onto the stack as the user drills down into details of the UI, and popped off one by one as the user's focus returns to a higher level view.

Developing an application with Mojo requires you to design the scenes with HTML, and implement controllers for those views in what Mojo calls scene assistants using JavaScript.

Further details of Mojo SDK development can be found at Palm's Developer site. Now, let's see how to integrate Brightcove's Media API with the Mojo SDK.

Integrating the Media API

The REST-based implementation of the Brightcove Media API makes it ideal for wrapping with a JavaScript class. There are already a few JavaScript APIs, but for this WebOS implementation, a different approach seemed to make sense. The provided API methods documented here can be easily wrapped like so:

/** 
 * Find Video by ID 
 * @param videoId id of sought video.
 */
Brightcove.prototype.findVideoById = function(videoId, options, caller, onSuccess, onFail) {
    Mojo.assertNotNull(videoId, "There must be a video id");

    options.video_id = videoId;
    this._sendFindRequest("find_video_by_id", options, caller, onSuccess, onFail);
}

Brightcove.prototype._sendFindRequest = function(method, options, caller, onSuccess, onFail) {
    Mojo.assertNotNull(this.apiToken, "The api token is not set.");
    Mojo.assertNotNull(method, "The method is not set.");
    Mojo.assertNotNull(caller, "The caller should be set.");
    Mojo.assertNotNull(onSuccess, "There should be an onSuccess callback.");
    Mojo.assertNotNull(onFail, "There should be an onFail callback.");

    var requestUrl = this.requestBase + method + "&token=" + this.apiToken;
    if (options != null) {
        for (var key in options) {
            requestUrl += "&" + key + "=" + options[key];
        }
    }

    var request = new Ajax.Request(requestUrl, {
        method: 'get',
        evalJSON: 'force',
        contentType: 'application/json',
        onSuccess: onSuccess.bind(caller),
        onFailure: onFail.bind(caller)
    });
}

In these two functions, findVideoById() provides a thin wrapper around the Media API REST method, find_video_by_id. The caller passes in all required arguments (in this case, just videoId) along with any additional options, along with the calling object, a success handler, and a failure handler. The wrapper adds the required argument to the option object and passes that to the formally private function, sendFindRequest. Note that in JavaScript because you cannot really declare a function private, developers often precede functions with "_" when those functions should not be called from outside the class.

The Mojo SDK includes the Prototype JavaScript framework, although you can use any JavaScript framework, by including the necessary files in your project. _sendFindRequest() iterates over the options, adding each property in options to the request URL. It then creates a new Ajax.Request object using the Prototype framework and makes the API call, passing the result to the calling object via the specified callback functions.

With this method, it's pretty simple to wrap Brightcove API calls. You can download a zip file of source code that provides these wrapper functions for all of the Media Read API functions. You can enhance them as your application requires.

Laying out the application

When the application starts, it checks its local storage for a Brightcove Media API token. If it cannot find one, it pushes the Preferences scene onto the stage to request the token from the user. A Media API token is a little tedious to type in, but if you were writing an end-user application, you would most likely embed your token in the source code, leaving the user unaware.

Assuming a token is available, the Home screen appears. The Home screen contains a search box for searching all videos in your account, a button for browsing all videos, and a list of all playlists. This is all laid out with HTML in app/views/home/home-scene.html:

<div class="palm-group">
  <div class="palm-group-title">Videos</div>
  <div class="palm-list">
    <div class="palm-row-first">
      <div class="palm-row-wrapper">
        <div class="textfield-group" x-mojo-focus-highlight="true">
          <div class="title">
            <div id="searchText" name="searchText" x-mojo-element="TextField"></div>
          </div>
        </div>
          <div id="searchButton" name="searchButton" x-mojo-element="Button"></div>
      </div>
    </div>
    <div class="palm-row">
      <div class="palm-row-wrapper">
        <div x-mojo-element="Button" id="latestVideosButton"></div>
      </div>
    </div>
  </div>
</div>

<div class="palm-group">
  <div class="palm-group-title">Playlists</div>
  <div id="playlistFrame" class="palm-list">
    <div x-mojo-element="List" id="playlistWgt"></div>
  </div>
</div>

<div id="loading-info">
	<div class="palm-scrim">
    <div class="buffering-animation-container">
	    <div id="requestSpinner" x-mojo-element="Spinner"></div>        
    </div>
  </div>
</div>

And here's what it looks like rendered on the Palm Pre:

Home screen

When laying out a scene with Mojo, you use <div /> tags that are either empty or that contain additional <div /> tags. The special Mojo elements and CSS styles are only applied to these <div /> tags.

There are many divs here, but each has a different WebOS-specific style, allowing the layout to be created. You can see the two different groups, labelled: Videos and Playlists. These are created with the 'palm-group' style and given a title with 'palm-group-title'. In the Videos group, we create a list with the 'palm-list' style. This creates a list of UI elements, that you can see in the Videos group.

The text field and buttons are added with the <div /> tags that contain the special Mojo attribute, x-mojo-element and assigned the values 'TextField' and 'Button' respectively. With Mojo, you only lay out your components in HTML. All of a component's definition, like the label on a Button, is set in the scene's assistant.

Initializing the scene with an assistant

The assistant for the Home scene can be found at app/assistants/home-assistant.js. Here is a snipppet from the Home assistant:

function HomeAssistant() {
    this.playlistInitialized = false;
}

HomeAssistant.prototype.setup = function() {
    // App Menu     this.controller.setupWidget(Mojo.Menu.appMenu, Videos.MenuAttr, Videos.MenuModel);

    /* setup widgets here */
    this.controller.setupWidget("searchText", {
            hintText: "Search Text",
            autoFocus: true,
            autoReplace: false,
            textCase: Mojo.Widget.steModeLowerCase,
            requiresEnterKey: true
       }, 
       this.searchTextModel = {
            value: ""
       });

    this.controller.setupWidget("searchButton", {
            label: "Search"
        }, {
            disabled: false,
            buttonClass: 'primary'
        });

    this.controller.setupWidget("browseVideosButton", {
            label: "Browse Videos"
        }, {
            disabled: false,
            buttonClass: 'secondary'
        });

    // Skipping some widget initialization here for code sample brevity. 
    // Setup listeners for Search Text and Browse Videos button.     this.controller.listen("searchText", Mojo.Event.propertyChange,
        this.searchVideos.bindAsEventListener(this));

    this.controller.listen("browseVideosButton", Mojo.Event.tap,
        this.videoRequest.bindAsEventListener(this));
}

Each assistant is a simple JavaScript class initialized with a constructor. Here, we can add the initialization of any instance variables that you might need. When WebOS creates an assistant, it immediately calls its setup() function to allow the assistant to initialize the widgets on the scene.

In our setup(), we first create the the application menu, which is available to the user by tapping on the drop-down at the very top-left of the screen. I have omitted the definition of the menu, but it is easy to find in the source code.

Next, we setup the Search text field with hint text when the field is empty, a text case property that forces all search text to lower case and makes the widget send a propertyChange event if the Enter key is pressed in the text field.

Then we setup the two buttons with labels and properties that indicate whether they should appear as default buttons. Because the user can press Enter in the Search text field to begin a search, we want the Search button to appear default, or primary. There are additional button properties to choose from if you consult the API documentation.

Finally, listeners are setup the Search text field and the Browse Videos button. This is done by calling the listen() function of the controller, with the id of the button, the event we're interested in, and the event handler function that is implemented later in this class. Notice the bindAsEventListener() function. With JavaScript, if we pass a function as an argument into an event handler, we also need to bind in the object that will represent the this object when the event handler is called. Usually, we just pass in this as the value, but we could pass in any object that we want to be the target of the event handler.

For brevity, I'll leave the rest of the class for exploration, along with the scenes and assistants for the Preferences and Video List scenes. Browse through the source code, trying different widgets and settings and you'll soon have the Mojo basics figured out.

Preparing your videos for WebOS

The Brightcove Browser application can play videos from your account on the device, but only if the videos are encoded properly. WebOS supports H.264 videos. Consequently, when you upload videos using the Media module, be sure to click Edit Settings to set the transcode settings. Select multiple renditions for your uploaded videos and choose H.264 encoding for both the H.264 and Other settings groups. Using multiple renditions ensures that a rendition optimized for playback on a mobile device is created.

Unfortunately, even with correctly formatted videos, you will only be able to play your videos on the device, not the emulator.  The Palm Emulator does not have a video codec, so an error will occur if you attempt to play a video. 

Developer Tools

When developing a WebOS application, it's most convenient to have an emulator on which to try your application before copying it to your device. The Mojo SDK comes with an emulator based on VirtualBox that allows you to test your application.

Palm provides an Eclipse plugin that you can downloaded from the WebOS Developer web site. This provides quick access to the SDK tools that package your app and install it on your device or emulator. There is also a bundle for TextMate, if you prefer that editor. Undoubtedly, there will soon be support in vi, emacs, and other editors.

Once you have your source code, you need to pack up and install your application on your device or editor. This is done with palm-package, which creates a .ipk file out of all the source code. Then, palm-install will copy the .ipk file to a device or emulator. You can also launch the application from the command line with palm-launch. Each of these commands can be run with the --help argument to get more information about the command. Of course, full documentation is available on the Palm Developer site. Here's what installing and launching the Brightcove Browser on the emulator would look like:

$ cd /path/to/parent/of/Brightcove
$ palm-package Brightcove
Creating package in /path/to/parent/of/Brightcove/com.brightcove.browser_1.0.0_all.ipk

$ palm-install com.brightcove.browser_1.0.0_all.ipk
connecting to device emulator (756625F3830D6AE200306DD6D07BB802499EF913:tcp:52232)
copying /path/to/parent/of/Brightcove/com.brightcove.browser_1.0.0_all.ipk to 
  /media/internal/developer/com.brightcove.browser_1.0.0_all.ipk on device 
  emulator (756625F3830D6AE200306DD6D07BB802499EF913:tcp:52232)
installing package /media/internal/developer/com.brightcove.browser_1.0.0_all.ipk on device 
  emulator (756625F3830D6AE200306DD6D07BB802499EF913:tcp:52232)

$ palm-launch -i com.brightcove.browser
connecting to device emulator (756625F3830D6AE200306DD6D07BB802499EF913:tcp:52232)
launching application com.brightcove.browser
inspecting application com.brightcove.browser

The -i parameter to palm-launch indicates that we want to run the Palm Inspector on the application to help debug. Palm Inspector is a little bit like Firebug, allowing us to see console output, layout details, etc. After launching the Brightcove Browser, the Palm Inspector can be launched from wherever the SDK was installed.

Developer Support and Conclusion

The WebOS developer site contains a detailed API Reference as well as support forums, an online magazine, a new IRC channel for immediate help, and more.

This introduction to development for WebOS provides a taste of general practices when developing an application with the Mojo SDK.  With a little exploration of the source code, you'll find that developing for WebOS platforms presents few obstacles for a developer comfortable with HTML and Ajax development.