I've been experimenting a bit with CouchDB again recently and started thinking more about what it means to see non-relational databases as different from rather than better than traditional relational databases. One idea that I wanted to explore is that these differences mean that we do not have to use these new technologies in the same way as a traditional database layer. A notable feature of CouchDB is that it delivers data over an HTTP connection, so it can deliver data to the web without the need to write a layer of software to go in front of it. It can also store files quite happily. This could hugely simplify the server side of many phone, tablet and Internet TV apps so I thought I would have a go at building an experimental proof-of-concept app for the Samsung Internet@TV platform that gets both its metadata and video files from a CouchDB server.
This is not meant to be a full enterprise video delivery solution or anything like that but a simple exploration of how these technologies might work together. In this this example I am running the CouchDB server on an Ubuntu box that is on the same local network as the Samsung Internet@TV enabled Bluray player that will consume data from it.
The first step is to create a CouchDB database (have a look at Starting to relax with CouchDB for quick introduction to CouchDB). I called the database "tvshows" an created a couple of test records that had title, description and length fields. These could of course contain more metadata if you wished. I then uploaded the video file as an attachment to the record. Unfortunately when I tried this through Futon I found that the file uploaded just fine but the file upload dialog got stuck and remained on the screen even when the upload had finished. However you can also upload a video using curl with a command like this:
curl -vX PUT http://[server]:5984/tvshows/[document id]/[what you would like to call the file]?rev=[value from _rev field] --data-binary @[file name on your machine] -H "Content-Type: video/mp4"
The app will show a list of available videos to the user and this list will be driven by a CouchDB view. That way we can send only the data needed to the client which is important as it is a memory-constrained device. Below is the Javascript code for the “map” function for a view with a design name of “index” and a name of “home” (the “reduce” function was left blank).
function(doc) {
var videoURL = "";
var videos = doc._attachments;
for (var filename in doc._attachments) {
videoURL = doc._id + '/' + filename;
}
emit(null, {'videoName': doc.title, 'videoDescription': doc.description, 'videoURL': videoURL});
}
This is not a very sophisticated function, it just pulls out the required fields for the app and generates part of the URL for the video file, which will be played directly from CouchDB. For the purposes of this demo app that is the server side done! If you were using this app on the somewhere other than you home network you would probably want to set up an Admin user for CouchDB and set up security in the database itself to make sure anonymous users can only read the contents of the database and not modify them.
The client app for this experiment was based on the Video app tutorial from the Samsung D Forum (if you read this blog regularly you may remember that I also based a Linked Data powered Internet@TV app on this tutorial too). The tutorial runs through how to make a video app that is powered by an RSS feed, but in this example we want it to talk directly to the CouchDB server. Fortunately this can be done without the need for any database drivers thanks to the fact that CouchDB delivers its data in JSON format over an HTTP connection.
Many browsers can parse JSON format data directly through a built in “JSON” Javascript object. Unfortunately the MAPLE browser used in the Samsung Bluray player that I have doesn't have this object built in. It is possible to read data sent by a server in JSON format into a Javascript variable using the eval() function, but this is potentially risky if the server has sent malformed response or there has been a security issue and malicious code has been sent instead of the JSON object. It is much better to parse the response so I used the json2.js parser library by Douglas Crockford which can be found at: https://github.com/douglascrockford/JSON-js. This gives makes the JSON object globally available to the Javascript engine in MAPLE and also provides us with a parse() method. This file needs to be copied into the Javascript directory in the project and loaded in the index.html file by adding these lines below the Common Widget API section:
Thanks to the way the tutorial has been constructed by Samsung we only need to change one file to get it to use a CouchDB data source rather than an RSS feed. The file in question is Server.js which performs an HTTP request for the data, parses the returned data and feeds it into the rest of the application in a data format that it understands. In this example the code to get the contents RSS feed has been replaced with code to get the contents of the CouchDB view that was created earlier and the code to parse the RSS feed has been removed and instead replaced with a call to JSON.parse(). The listing for Server.js is below, remember to change the server name appropriately!
var Server =
{
/* Callback function to be set by client */
dataReceivedCallback : null,
XHRObj : null,
couchdb_url: 'http://[YOUR COUCHDB SERVER]:5984/tvshows/',
home_index: '_design/index/_view/home',
ratings_url: 'http://[YOUR COUCHDB SERVER]:5984/tvratings/'
}
Server.init = function()
{
var success = true;
if (this.XHRObj)
{
this.XHRObj.destroy(); // Save memory
this.XHRObj = null;
}
return success;
}
Server.fetchVideoList = function()
{
if (this.XHRObj == null)
{
this.XHRObj = new XMLHttpRequest();
}
if (this.XHRObj)
{
this.XHRObj.onreadystatechange = function()
{
if (Server.XHRObj.readyState == 4)
{
Server.createVideoList();
}
}
alert( this.couchdb_url + this.home_index );
this.XHRObj.open("GET", this.couchdb_url + this.home_index, true);
this.XHRObj.send(null);
}
else
{
alert("Failed to create XHR");
}
}
Server.createVideoList = function()
{
if (this.XHRObj.status != 200)
{
Display.status("Server Error " + this.XHRObj.status);
}
else
{
var results = JSON.parse(this.XHRObj.responseText);
var videoNames = [ ];
var videoURLs = [ ];
var videoDescriptions = [ ];
for (var i in results.rows) {
var values = results.rows[i].value;
videoNames[i] = values.videoName;
videoURLs[i] = this.couchdb_url + values.videoURL;
videoDescriptions[i] = values.videoDescription;
}
Data.setVideoNames(videoNames);
Data.setVideoURLs(videoURLs);
Data.setVideoDescriptions(videoDescriptions);
if (this.dataReceivedCallback)
{
this.dataReceivedCallback(); /* Notify all data is received and stored */
}
}
}
In a production case you would probably want to add lots of error checking to this, but hopefully it shows that once you have the JSON object imported it is pretty easy to work with data from a CouchDB data source. You will notice too that the videos are played directly from CouchDB, no need for a separate web or file server, which cuts down the complexity on the server side.
I tried running the app on the emulator that comes with the Samsung Internet@TV SDK and then on the bluray player itself. In both cases the app seemed to work well and CouchDB worked very well as the server. It would be interesting to try this with lots of video files and lots of clients to see how it copes under strain. Writing data back to the server could also be useful, maybe when a show is watched this could be noted anonymously in a record and a map/reduce view used to generate ratings reports, or the popularity of client devices.
Building this demo app has been an interesting exercise as it is shown me how useful CouchDB potentially is for app developers. Maybe delivering video is an extreme case but many apps have data needs that CouchDB could meet in a relatively easy to set up way. This sort of application architecture plays to CouchDB's strengths, there are other cases where maybe it would not work very well but hopefully this example shows that technology like this provides us with extra options when thinking about the architecture surrounding apps.
Photo: Frontiers 2011 - Smart&App La3 TV by frontiersofinteraction