Roku Screensaver Pt. 2

A friend of mine likes to say that no aspect of a programming language is an accident. If the designer of a language excludes a feature or includes certain features but not others, it is because the designer is trying to encourage use of the language in a certain way. Look up discussions of pythons lack of increment and decrement operators for an example. While I don’t share my friends faith in the foresight and design wisdom of language creators; I do think it is an excellent way of looking at language limitations. Instead of seeing them as failures that hamper us, view them as guides trying to direct us.

In high level scripting languages such as BrightScript, this tends to come up a lot. The Script designers often have to make choices about what functionality to provide access to, or what components and global functions to develop. In BrightScript I’ve come across two examples of this; limits in their regular expression component and their file system.

roRegex

roRegex is the BrightScript component for handling Regular Expressions. Each roRegex object you create is assigned a regular expression at creation, and by passing it a string you can do accomplish various things you would expect to be able to do with regular expressions. One surprise is that roRegex.Match(StringToBeSearched) returns an Array, but only contains the first match. ReturnedArray[0] contains the full match, while ReturnedArray[1…n] contains the results of the subexpressions for the first match. However roRegex has no direct way of iterating through all matches of the regular expression in StringToBeSearched.

At first I thought this was a gross oversight on the part of BrightScript. It seems incredulous that they could recognize the need for Regular Expressions and then hamstring them in such a way. However Regular Expressions can be very expensive. In one example, I found that processing “(.+)FooBar” vs “(\w)FooBar” in a 42k character document was a 5 second difference. BrightScript appears to use Perl regular expressions, which are documented to use a common implementation that has some pretty bad edge cases. Additionally, BrightScipt’s design philosophy is to be the point where various pieces provided externally come together and are presented to the user. Heavy processing is supposed to be done on the service provider’s servers, no on the Roku itself. By limiting the number of matches returned by roRegex.Match(), BrightScript encourages the developer to do their heavy lifting on different hardware while still providing basic functionality.

Unfortunately, I’m not likely to get Flight Rising to provide an API that I can tap into, and writing a web service to do this processing is out of scope for this project. Thankfully, the work around is fairly simple. We just need to break the StringToBeSearched up so that each segment contains one copy of the expression we are looking for. Both roString.Tokenize() and roRegex.Split() can accomplish this; roRegex.Split() has the advantage of allowing us to use multiple characters to act as delimiters. If our goal is to find all cases of “FooBar(\w)”, we can start by splitting the string using “Foo”. This will ensure that “Bar(\w)” is the first thing roRegex.Match() sees, reducing search times and avoiding conflicts with other potential matches.

roFileSystem

roFileSystem is the BrightScript component for accessing the File system. BrightScript’s sandbox ensures you won’t be seeing any Roku FileManager Apps, but roFileSystem has enough of the basics that you could. Unfortunately, one thing roFileSystem doesn’t tell you is how much space is available. You can see how much space is available on external drives such as a USB dongle, but not on the provided internal storage. Which, if you’re trying to create a cache to manage files you’ve downloaded from the server can be a big deal.

In this case, the reason for the limitation lies in how the Roku manages all of it’s file space. To begin with, Roku Apps that are subscribed to aren’t guaranteed to be installed. Depending on size, frequency of use, and demands for space by other applications, the Roku will uninstall apps as necessary, and then reinstall them when they are needed. Then, once space taken up by external apps is accounted for, we have to worry about space consumed by streaming audio and video. Given that the Roku’s primary purpose is to display streaming data, it makes sense that this data has priority over all other storage. What is less clear is what this means for data stored in the running applications “tmp:/” space. A poorly written cache could fill the “tmp:/”. Leaving that file space untouched could cause issues with steaming playback, but arbitrarily removing files from it would lead to unpredictable problems with the application. I believe that BrightScript doesn’t clean up “tmp:/” until application exit, but this means that an application author needs to be careful about using too much “tmp:/” space.

BrightScript’s solution to this is roTextureManager, which acts as a LRU Cache for image files, and is managed behind the scene’s by the Roku. You use a roTextureRequest component to request an image by it’s URL, and roTextureManager does the work of seeing if it’s already saved locally, and then acquiring it if it is not. It’s easier to implement than downloading and processing the image file using roUrlTransfer and roFileSystem. roTextureManager, however, is very poorly documented and reportedly it’s error handling is unreliable. It was also unclear if roTextureManager components all accessed the same LRU Cache manager, or if each instance of the component had their own LRU Cache to manage. In the end I went with a single roTextureManager saved in the Global Associative Array, and a simple “succeed or timeout” error catching.

Project Status

At this point; the Screensaver is almost complete. I’ve started working on the options interface; which has presented some unique challenges as a lot of the provided screens don’t follow the “stack” paradigm that BrightScript screens are supposed to follow. The screensaver’s error handling is fairly robust and seamlessly handles various external failures. Our calls to FlightRising are minimized, and there are no perceptible delays in start up or execution. I’ve published the 2nd development version for testing. Once the options interface is complete, I’ll do a refactor and documentation pass, and publish the final result.