What I’m Working On: Tweet to the middle of a video

Recently, I’ve been doing some work with Mozilla around their Web Made Movies project. They’ve been experimenting with the integration of video and the Web enabled by the multimedia features available in modern browser and building tools like Popcorn.js. As we’re starting to take some of those projects out of the lab and into the real world, it’s been interesting to see how those tools hold up and what features are inspired by these situations.

With just a few hours advance notice, the Popcornjs crew put together a video/data mash-up of President Obama’s State of the Union speech for PBS, in which text analysis is displayed in time with the video. Among such a long video, with so much data, WMM leader Brett Gaylor asked if I could build in a feature making it easy to Twitter a short URL right to a point in the middle of the video. Sure I could. Unfortunately, an external service we were using to access the Twitter API broke down, so we couldn’t get the feature working in time to go live. I’ve worked around it, and here it is now.

Watch the video from the beginning or start 20 minutes in. Try the button just below the video. Read on to learn how it works.

The process is pretty simple. (I decided to use jQuery, since it was already loaded up for Popcorn.js, but it’s not necessary.) The three main steps are:

  1. Check the URL for a time code and advance the video to the appropriate starting point.

  2. Update the time code on the button.

  3. Set up a “click” handler on the button to generate the link and open a Twitter window.

Here is the code to get the video started at the right place:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* get the <video> element. Don't use jQuery here because it doesn't expose video properties and methods */
var video = document.getElementById('video');

/* advance video and auto-play */
var startVideo = function() {
    if (targetTime < video.duration) {
        video.currentTime = targetTime;
        video.play();
    }
};

/* parse URL hash after # for a number */
var targetTime = window.location.hash.substr(1);
targetTime = parseFloat(targetTime);
if (!isNaN(targetTime) && targetTime > 0) {
    if (video.readyState >= 3) {
        startVideo();
    } else {
        $('#video').bind('loadedmetadata', startVideo);
    }
}

The information we need here is how far into the video we want to stop. To keep it simple, we’re just looking for a floating point number to represent seconds. Since all this processing is happening in the browser and I don’t have access to the server anyway, we put the number in the “hash”, which is the part after the # symbol.

After checking to make sure we have a positive number, we test if the video is loaded, since we don’t always know the order in which our media and scripts will be loaded. If the video is already loaded, start up the video right away. Otherwise, bind our function to the “loadedmetadata” event so the browser will get back to us when it’s ready to start the video. By using an event handler, we don’t have to set a timeout to keep checking every few milliseconds. (Though I’ve been told “loadmetadata” can be slow to fire, so some people use a timeout, even though it’s ugly.) Once the metadata has loaded, we can find out how long the video is to make sure we’re not fast-forwarding past the end, and then it’s simple to set the time and start playing.

Next, update the time code on our button. (It’s not actually a button, just a div, but it works anyway.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$('#video').bind('timeupdate', function(event) {
    var time = this.currentTime;
    if (time < 0) {
        time = 0;
    }

    var mins = Math.floor(time/60,10);
    var secs = Math.floor(time - mins*60);
    var hours = Math.floor(mins/60,10);
    secs = (secs > 9 ? secs : '0' + secs);

    if (hours > 0) {
        mins = mins % 60;
        mins = (mins > 9 ? mins : '0' + mins);
        $("#tweet-time").html('Tweet to ' + hours + ':' + mins + ':' + secs);
    } else {
        $("#tweet-time").html('Tweet to ' + mins + ':' + secs);
    }
});

This feature is not absolutely necessary to make this work, but it’s helpful to tell the user exactly how far into the video we’re linking and indicate that the button action is time-sensitive. The “timeupdate” event fires a few times a second, whether we’re playing or scrubbing the video, forward or reverse. Javascript doesn’t provide an easy way to make a string showing elapsed time from seconds, so we do it ourselves. jQuery makes it easy to change the text.

One problem with this code is that the button changes width as the time changes. Ideally, you’d probably check the duration of the video once at the beginning to determine whether you’re ever going to need the hours, and pick a fixed-width font.

Finally, set up the “click” event to create the link and pass it to Twitter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var base = window.location.protocol + '//' + window.location.host + window.location.pathname;
$("#tweet-time").click(function() {
    var time = video.currentTime;
    if (time < 0) {
        time = 0;
    }

    var height = screen.height;
    var width = screen.width;

    window.open('http://twitter.com/share?count=none&counturl;=' +
        encodeURIComponent(base) + '&text;=&url;=' + encodeURIComponent(base + '#' + time),
        'tweet-time-sotu', "left=" + Math.round((width/2)-(550/2)) +
        ",top=" + Math.round((height/2)-(450/2)) +
        ",width=550,height=450,personalbar=0,toolbar=0,scrollbars=1,resizable=1");
});

The most important point here is creating the URL to pass along to Twitter (“base + ‘#’ + time”). One thing we need to be very careful about is that most Twitter clients parse everything after “#” as a hashtag, separate from the URL. The solution is to use a [URL shortener, which is handy for hiding the “#” symbol as well as saving us characters out of the allowed 140. Originally, I used the is.gd API for this (we need a different short URL for every timecode someone uses). This worked well enough, but it required an extra call out to the network, which made things crawl on a slow connection.

Fortunately, Twitter provides a nice, clean sharing tool that will create a short URL for us. (This page explains how to use it.) We just have to make sure to wrap our target URL in encodeURIComponent so the “#” gets escaped, otherwise everything after it won’t make it to the server. Normally, I hate using pop-up windows, but Twitter was pulling some kind of referrer-checking shenanigans, giving me a “Forbidden” server error when I tried to load this page inside an