Communicating with Web Services from BrightScript

In the last few blog posts, we’ve been looking at implementing fundamental functionality in BrightScript channels.  This trend continues in this installment.  This time around, we will be exploring how to communicate with web services from BrightScript.  Web service interaction is essential to BrightScript channel development, as the majority of the content consumed by the Roku Streaming Player is hosted on the Internet.  To demonstrate the basics of consuming web services from BrightScript, we will use a simple channel that loads and plays free content from The Khan Academy.  For purposes of this article, we will assume that you already understand the basics of HTTP, XML and JSON data formatting, and the concepts around web APIs such as REST.  This article is meant to illustrate taking advantage of such technologies from BrightScript.  The complete details of the Khan Academy API can be found here: Khan Academy API

Keep in mind that every web service enabled channel is going to look very different, access different services, and use data in a variety of ways.  However the code in this article and in the accompanying sample channel are fairly typical of this type of channel, and as such will give you a good sense of the general approach to communicating with web services from BrightScript.

The roUrlTransfer Component

The BrightScript component used to communicate with remote servers is called roUrlTransfer.  Using this component, channels can perform any HTTP requests (GET, POST, etc.) and of course read data returned in HTTP responses.  Requests can be made either synchronously or asynchronously.  The component can also perform mutual authentication with the server hosting the content. Perhaps the simplest operation you can perform with roUrlTransfer is an HTTP GET:

    request = CreateObject("roUrlTransfer")
    request.SetUrl("http://blog.roku.com/developer")
    html = request.GetToString()

request is an roUrlTransfer instance.  The SetUrl function specifies the URL to which request will connect.  Finally, the GetToString function synchronously issues the actual HTTP request and blocks until the remote server returns.  The return value of GetToString contains the HTTP response.  Data can be posted to web resources as well using the PostFromString function:

    Integer PostFromString(String request)

This function performs an HTTP POST to the url specified by calling SetUrl.  The string argument contains the data that you want to POST to the specified resource.  In most cases, when communicating with web services, you will be doing GET requests to pull video feeds and perform other such operations exposed by web service APIs.

Asynchronous HTTP Requests

In many cases you will not want HTTP requests to block.  roUrlTransfer can easily make asynchronous requests.  For example, to make an asynchronous HTTP GET request, you can use the roUrlTransfer function AsyncGetToString:

    Boolean AsyncGetToString(void)

This function makes an HTTP GET request to the url specified by SetUrl.  The function returns immediately.  The return value indicates whether the request was sent successfully.  In order for your channel to detect when the remote server has returned a response, you need to assign an roMessagePort to the roUrlTransfer instance that made the GET request.  Your BrightScript code can then wait on that port, testing for the appropriate response codes.

In our sample, wait is called with a timeout of one second.  If the HTTP request completes in that timeframe, wait returns an roUrlEvent.  The BrightScript code then checks the response code, and if the request was successful, proceeds to parse the returned JSON.  If the HTTP request doesn’t respond within one second, the wait call will time out and return invalid.  In this case we can assume that the API server is unresponsive and we cancel the request by calling the roUrlTransfer function AsyncCancel.

Under the covers roUrlTransfer is implemented with curl.  Error codes that are reported to your roUrlTransfer event loop are therefore CURL codes.  roUrlTransfer events have the type roUrlEvent.  Details of the response codes that can be returned are found here: roUrlEvent Response Codes

As an example of making an asynchronous request, let’s say that we want to make a call to the Khan Academy API.  In this sample, we request all of the video playlists in the Khan Academy library.  This is done by calling the API http://www.khanacademy.org/api/v1/playlists.  If the request is successful, the HTTP response will contain a JSON formatted list of all of the playlists.

Function get_playlist() as object
    request = CreateObject("roUrlTransfer")
    port = CreateObject("roMessagePort")
    request.SetMessagePort(port)
    request.SetUrl("http://www.khanacademy.org/api/v1/playlists")
    if (request.AsyncGetToString())
        while (true)
            msg = wait(0, port)
            if (type(msg) = "roUrlEvent")
                code = msg.GetResponseCode()
                if (code = 200)
                    playlist = CreateObject("roArray", 10, true)
                    json = ParseJSON(msg.GetString())
                    for each kind in json
                        topic = {
                            ID: kind.id
                            Title: kind.standalone_title
                        }
                        playlist.push(topic)
                    end for
                    return playlist
                endif
            else if (event = invalid)
                request.AsyncCancel()
            endif
        end while
    endif
    return invalid
End Function

There is a lot going on here.  First we create an roUrlTransfer instance, called request, and assign an roMessagePort to it.  We then set the URL of request to the web service API we want to call.  We then issue the HTTP request by calling AsyncGetToString, and wait on the message port assigned to request.

If the API request is successful and returns a status code of 200 (HTTP OK) the HTTP response from the API will be a JSON formatted list of all of the topics in the KhanAcademy library.  A typical playlist request looks like this (click the image to enlarge)

Khan API JSON Response

The response consists of a number of topics containing the unique id, title, description, etc. for a specific Kahn Academy topic.  Our BrightScript code accesses this response by calling the msg.GetString function.  This response will be in raw JSON string format.  It can be converted to roAssociativeArray format by passing it to the BrightScript ParseJSON API.  Our get_playlist function iterates through each topic in the JSON response, creating a topic object for each, and putting each of these in a topics array.

Not all web services pass data using JSON format.  Many services still use some form of XML for communication.  If this is the case for the service you need to interact with, the BrightScript’s native XML support would be used in place of the ParseJSON API.  For more on the BrightScript JSON API, see this article: BrightScript JSON Support  For more information about BrightScript XML support, look here: BrightScript XML Support

Additional web service API calls can be made from the BrightScript code in the same way.  Simply use the roUrlTransfer component to target the right API URL, make the request, and parse the response.  As another example, let’s look at how you can use the results of the previous API call to load the videos for a given topic in the playlist.

As we saw, the playlist response consists of a collection of topic objects.  Each of these topics has an id property that can be used in other Khan Academy APIs to load various details for that topic.  To load the videos for a topic, call the API

http://www.khanacademy.org/api/v1/topic/[topic_id]/videos

For example, here is how our channel could load all of the videos for the absolute value topic.  This code is very similar to the code above for loading the playlist.  We use and roUrlTransfer instance to make the HTTP request, wait for a response from the API service, and then parse the returned data according to the needs of our channel.  In this case the video information is loaded into an array for use in a poster screen UI.

Function get_topic_videos(id as String) as object
    request = CreateObject("roUrlTransfer")
    port = CreateObject("roMessagePort")
    request.SetMessagePort(port)
    request.SetUrl("http://www.khanacademy.org/api/v1/topic/absolute-value/videos")
    if (request.AsyncGetToString())
        while (true)
            msg = wait(1000, port)
            if (type(msg) = "roUrlEvent")
                code = msg.GetResponseCode()
                if (code = 200)
                    videos = CreateObject("roArray", 10, true)
                    json = ParseJSON(msg.GetString())
                    for each kind in json
                        video = {
                            Title: kind.title
                            ShortDescriptionLine1: kind.description
                            Description: kind.description
                            Views: kind.views
                        }
                        if (kind.download_urls <> invalid)
                            video.SDPosterURL = kind.download_urls.png
                            video.HDPosterURL = kind.download_urls.png
                            video.Url = kind.download_urls.m3u8
                        endif
                        videos.push(video)
                    end for
                    return videos
                endif
            endif
            if (msg = invalid)
                request.AsyncCancel()
            endif
        end while
    endif
    return invalid
End Function

To dive deeper into using WebServices from BrightScript, download the sample Khan Academy channel from this link:
Sample Channel
Feel free to add API calls to add additional functionality to test out your knowledge.  Happy Coding!

About Robert Burdick

Roku Developer Support Manager
This entry was posted in Uncategorized. Bookmark the permalink.
  • Klimov

    aoeworld.com