Because every situation is different and - for obvious reasons - I can't give a truly working example, I'm just going to present code snippets of how this is done.
Storing Credentials
I'm not going to discuss the UI of getting user credentials, but it's painful enough to do that you aren't going to want to ask your users to do this more than once. You will then want to store them.It seems the preferred approach to "storing" things on a Roku is to place them in the "Repository" which is a small amount of memory put aside for "your application" (technically, all applications published by the same publisher). By "small amount", I mean 16K. Now, I succesfully programmed a TRS-80 for three years with nothing more than 12K but by today's standards - particularly when you are talking about a streaming device that buffers - 16K is nothing. But there it is. It is certainly enough to track user credentials - and from there you could store everything off-premises.
There is an roRegistry object in Roku, but it seems that for our purposes you don't actually want that. The Repository is divided into sections and the roRegistry is just there to enable you to remember which sections you created. Instead, we want to go straight for an roRegistrySection which is where we can store individual key/value pairs.
We can create a section and store credentials so:
auth = CreateObject("roRegistrySection", "Authentication")auth.Write("username",username)auth.Write("password",password)
Here it is assumed that you have already done the work of obtaining the user's credentials and just want to store them. Note that if the "Authentication" section already exists, this retrieves it and overwrites the fields.
In the same way, we can read the credentials back later, probably in a different part of the code:
auth = CreateObject("roRegistrySection", "Authentication")username = auth.Read("username")password = auth.Read("password")
Depending on how you structure your code, you can choose to share the roRegistrySection or not.
Creating an HTTP Agent
Internally, obviously, Roku does a lot of stuff with HTTP. As we saw with the video example, you can create a "content node" with a URL, point a video player at it, and you will be streaming in seconds.
But what if you want to do more pedestrian, browser-like HTTP? For that there are roHttpAgent and, in particular, roUrlTransfer objects. An roUrlTransfer object is like an embedded browser, HttpClient in Java or XMLHttpRequest in Javascript.
In essence, it's very easy to use:
m.http = CreateObject("roUrlTransfer")m.http.SetUrl(fullUrl)res = m.http.GetToString()
which returns the body of the response as a string.
It's also possible to do an AJAX-like call by specifying AsyncGetToString():
m.http = CreateObject("roUrlTransfer")m.http.SetUrl(fullUrl)m.http.SetMessagePort(m.port)sent = m.http.AsyncGetToString()
This just returns a boolean to indicate whether it was able to initiate a transfer. The important thing here is that, before sending the request, we attach the object to a "message port" - i.e. an event handler. The system will then send this port an roUrlEvent when the response - success or failure - comes back.
We already have an event loop so we can just add code to it. We end up with a structure something like this:
while(true) msg = wait(0, m.port) msgType = type(msg) if msgType = "roUrlEvent" body = msg.GetString() end ifend while
Now, in order to do authentication, we (generally) need to do a POST rather than a GET and we need to enable cookies on the roUrlTransfer object. For some reason, a synchronous POST operation is supported but it is impossible to extract the body. Since we will probably want some portion of the body, we need to do asynchronous requests. Updating our code from before, we can do this:
m.http = CreateObject("roUrlTransfer")m.http.SetUrl(postUrl)m.http.EnableCookies()m.http.SetMessagePort(m.port)m.http.AddHeader("Content-Type","application/x-www-form-urlencoded")sent = m.http.AsyncPostFromString(body)
Note that we have explicitly enabled cookies here and also specified a content type. This latter is a concern of the server so you will need to check what it is expecting, but obviously it should match the format of the body that you are sending.
Cookies
If you actually want the cookies you can obtain them from the roUrlTransfer object while handling the response as follows:
m.cookies = m.http.GetCookies(domain,"/")
This returns an associative array of cookies which is somewhat tricky to manipulate but I'm sure you can figure it out (hint: you probably don't actually need any more than this).
XML
Your body may come back in many different forms (HTML, XML, JSON, ...). Because mine came back as XML, I looked into that case and not any of the others.
You can parse XML by creating an XML element. Note that the XML element is "stateful" in that you call parse() on the element and it updates its internal state. This isn't that big a deal; it's just that it's not the way that parsers in Java and Javascript work; there you create a parser explicitly and it creates elements as it parses the text.
xp = CreateObject("roXMLElement")xp.Parse(body)
You can then traverse the body in much the way you would expect, although remember that this is an element not a document and thus it has the outermost name returned in the XML. Thus when you call getNamedElements(name) the elements are the ones embedded in the document element.
attrs = xp.GetAttributes()name = xp.GetName()elts = xp.GetElementChildren()namedElts = xp.GetNamedElements("Tag")hasAtt = xp.HasAttribute("x")
Strangely, there isn't a GetAttribute method - I don't know why not. But it's easy enough to write one:
function GetAttr(elt, attr) attrs = elt.GetAttributes() for each a in attrs if a = attr return attrs[a] end if end forend function
This returns the value "invalid" (like null in Java or undefined in Javascript) if it drops through to the bottom.
TLS
For secure websites, Roku documentation says you need to add the following magic. I didn't investigate any of this, just did it. If this doesn't work for you, there is documentation around this topic.
m.http.SetCertificatesFile("common:/certs/ca-bundle.crt")m.http.AddHeader("X-Roku-Reserved-Dev-Id", "")m.http.InitClientCertificates()
Sharing your Cookies
Having successfully logged in and recovered a token or cookie, you need to attach it to the video player. Although, you don't actually attach it to the video player for reasons that become clear if you think about it long enough; instead, you attach them to the content node which has the secure URL in it. Thus, if you have a content playlist, each one can come from a different secure website with its own cookies and headers.
The content node seems to be generally poorly documented in the Roku documentation. Probably the best I've found is this buried in the video documentation. Comment if you have a better reference.
Anyway, as written there, you can configure your content node from your roUrlTransfer object by just copying the fields:
m.content.url = urlToPlaym.content.HttpCookies = m.http.GetCookies(domain,"/")m.content.HttpCertificatesFile = "common:/certs/ca-bundle.crt"m.content.HttpHeaders = ["X-Roku-Reserved-Dev-Id:"]