Strophe and Greasemonkey

This week I’ve been looking at using Strophe and Greasemonkey to try to make an evocative but simple two-screen prototype for web-based on demand video. It was a bit fiddly in parts but it’s very useful once you get it going. Danbri has been building prototypes like these for some time, but it’s taken me this long to get round to having my own version working because you need your own XMPP server (that’s not strictly true, but more on that later). R&D Prototyping team have also recently been creating demonstrations with a similar setup. This is a technical blog post which should help you create a similar setup if you are interested in this technique.

Why are we doing this?

In the NoTube social web area we are experimenting with using XMPP for communication between devices, primarily because we want to look at social APIs for TV – and XMPP provides a lot of the infrastructure we need there (such as friends, groups, messaging). First we need some basic control of video, so before we start putting friends into the mixture we are implementing some of the usual remote commands such as play / pause, and also ‘nowp’ (now playing).

We’ve done this with an XMPP python bot talking to MythTV and an iPhone as a controller, but it was slow work to build the iPhone interface, and the client code isn’t usable on other non-Apple devices.

Danbri introduced me to Strophe, which is a Javascript API for XMPP, which uses BOSH or similar to interface with an XMPP server (such as eJabberd or OpenFire). The upshot of this is that you can control one webpage from another using XMPP, using no server-side Web code. This is great for quick prototyping, and for demonstrating control of web-based video. What’s even cooler for demos like these is to use Greasemonkey on the player side, in which case you can show demonstrations based on real video sites, as Greasemonkey gives you control over the behaviour and appearance of the video site (only on a machine with the script installed – see Greasemonkey – installing scripts – but that’s fine for demos).

Getting it working

The basic process is this:

  • Create a domain (because of javascript’s access control policy)
  • Install jabber server
  • Create two accounts and make them friends
  • Put an http rewrite file in the right place
  • Create ‘near’ and ‘far’ webpages to represent the remote and player
  • Create a Greasemonkey script and iFrame for the web based video player

A word of warning: this is fiddly. Things are changing in the APIs in browsers, in particular, cross-domain iframe loopholes are being closed and browsers are using the postmessage system instead, which is fine, as long we you realise what’s going on (and can control the iframe content, which is why we need Greasemonkey).

Here’s each step in a bit more detail.

Fix domains

By far the least confusing way of getting this working is to create a new domain, e.g. jabber.yourdomain.com. You’ll need to fix the DNS with your provider and then edit apache virtual hosts file (for me on ubuntu it’s /etc/apache2/sites-enabled/000-default), for example:

<VirtualHost *:80>
ServerName jabber.notu.be
DocumentRoot /var/www/jabber
<IfModule mod_proxy.c>
ProxyPreserveHost On
</IfModule>
<Directory /var/www/jabber>
DirectoryIndex index.html
AllowOverride All
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

and then restart apache

sudo /etc/init.d/apache2 restart

Install jabber server

In Ubuntu this is as straightforward as ‘sudo apt-get install ejabberd‘. OpenFire is also apparently easy.

Configure jabber server

pico /etc/ejabberd/ejabberd.cfg (yeah I use pico! got a problem? :-) )

edit this:

%% Hostname
{hosts, ["jabber.yourhostname.com"]}.

You need quite a recent version of ejabberd (later than 2.0), and then it includes BOSH.

Create two accounts and make them friends

Install mod_ctlextra:

http://www.ejabberd.im/mod_ctlextra

mkdir ejabberd
cd ejabberd
svn co https://svn.process-one.net/ejabberd-modules
cd ejabberd-modules/mod_admin_extra/trunk
./build.sh
cd ebin
sudo cp mod_admin_extra.beam /usr/lib/ejabberd/ebin/
ejabberdctl restart

On the commandline:

ejabberdctl register near jabber.yourhostname.com password
ejabberdctl register far jabber.yourhostname.com password
ejabberdctl add-rosteritem jabber.yourhostname.com near jabber.yourhostname.com near buttons both
ejabberdctl add-rosteritem near jabber.yourhostname.com far jabber.yourhostname.com far buttons both

Put an http rewrite file in the right place

The simplest thing to do is this:

Create a new directory in the correct domain

For example

cd /var/www
mkdir jabber
cd jabber

The directory can be called anything, but it needs to match your apache vhost config above.

Create an .htaccess file in that directory

pico .htaccess

RewriteEngine On
RewriteBase /var/www/jabber
RewriteRule http-bind http://localhost:5280/http-bind/ [P]
RewriteRule http-bind/ http://localhost:5280/http-bind/ [P]
RewriteRule http-poll http://localhost:5280/http-poll/ [P]
RewriteRule http-poll/ http://localhost:5280/http-poll/ [P]
RewriteRule admin/ http://localhost:5280/admin/ [P]

This means that the http interface to ejabberd is in the correct domain. If you put your ‘near’ and ‘far’ files in this directory or a subdirectory, that should work.

You mght need to enable rewrite and proxy modules in apache.

Create ‘near’ and ‘far’ webpages to represent the remote and player

You can see examples here: near, far.

Each has an on_message function and also sends messages. It’s all pretty straightforward. Info/query (iq messages rather than chat messages) are what we should be using to pass messages and they are a little bit more fiddly, but I’ve put some examples here: near, far. You’ll need strophe.js in the same directory.

Near sends load and play messages, while far sends nowp (now playing).

At this stage you should be able to see these two pages communicating. Load ‘near’ into Safari or similar and far into Firefox (3). Click play or load in ‘near’ and you should see a response in ‘far’.

Create a Greasemonkey script and iframe for the web based video player

We want the ‘far’ strophe page to control a player from a different site. For this you can put the player site as an iframe into the ‘far’ page.

Diagram showing the components and how they communicate

iFrames are confusing and using Greasemonkey with iFrames is even more so. In modern browsers, iFrame-based hacks for communicating between cross-site iFrames are not allowed, so you need to use HTML5’s postMessage instead. This is much easier.

For the iFrame player Greasemonkey script to post to the enclosing ‘far’ page do this:

unsafeWindow.parent.postMessage(message, "http://jabber.yoursite.com/");

where message is a string.

The ‘far’ page checks for messages like this:
function receiveMessage(event){
alert(event.data);
}

The far page can also send messages like this:

var win = document.getElementById("frameid").contentWindow;
win.postMessage("pause", "http://playersite.com/");

and the Greasemonkey script uses receiveMessage as before.

The ‘unsafeWindow‘ is a way to get at the real window of the iFrame (to bypass Greasemonkey restrictions). Using this you can also call local javascript methods:

unsafeWindow.play();

Using near, far and test_greasemonkey.user.js, with test.html on a different server (all in this directory), you should be able to send messages from near that affect test.html. Then all you need to do is adapt it to the site you are interested in controlling.

CORS (Cross Origin Resource Sharing)

In suported browsers, CORS allows you to make cross-domain ajax calls using javascript. This means that you should be able to host ejabberd on a different domain to the files that use it. In general CORS is easy to implement, but I haven’t tried it in this application yet.

Links

Update – May 16 2011

I recently had to install ejabberd elsewhere and forgot how incredibly fiddly it is to get working. So here’s a few gotchas and possible solutions:

Problem: ejabberd won’t start

Try starting it as root user (not just sudo):


su root
ejabberd start

Problem: ejabberd won’t start in background


ejabberd start -detached

Problem: running ejabberdctl gives an error like “Failed RPC connection to the node ‘ejabberd@localhost': nodedown


su root
ejabberdctl

Problem: bosh not working, even though the page /http-bind says it is

Edit modules part of config make sure you have right config! there are two! – check

ps ax | grep ejabberd

and that will show the correct one. The modules section should contain:


{mod_http_bind, []},
{mod_http_poll, []},

Problem: Mysteriously still not working, on Amazon EC2

Make sure the right ports are open: 5222, 5223, 5280 – on EC2 you have to open these using for example the configuration dashboard. It’s under ‘security groups’

Problem: Mysteriously still not working

HTML client must be on the same domain as http-bind

This entry was posted in Code. Bookmark the permalink.

3 Responses to Strophe and Greasemonkey

  1. Dan Brickley says:

    Thanks for writing this up :)

    Another ingredient to mention here is flXHR; this is a more-or-less drop in replacement for Javascript/browser XML HTTP Request, but which routes things via a tiny Flash app. Flash has different cross-domain constraints than Javascript (a permissioning file can be used). Strophe.js comes with flXHR support built in, however I can’t say I’ve gotten it working yet (or found much documentation).

    Talking of strophe.js documentation, I do recommend folk take a look at Jack Moffit’s http://professionalxmpp.com/ (there’s a free sampler chapter online there too, “Chapter 14: Writing Strophe Plugins”).

  2. Dan Brickley says:

    ps. I forgot to mention; one case in which these x-domain constraints become quite relevant and tricky is when the video is playing within a social network site via an externally loaded OpenSocial app (or perhaps Facebook app). That was in fact the use case which drove me originally to look into flXHR…

  3. Pingback: N-Screen backend: XMPP/Jabber and group chats | NoTube

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s