{ Hi! I'm Mike }
I'm a core developer with The Horde Project and a founding parter of Horde LLC - the company behind the world's most flexible groupware platform. This is my personal blog full of random thoughts about development and life in general.
January 10, 2009

When two itches collide

Recently, I've been working on a Horde library to interface with the Vimeo video service. It only works against Vimeo's Simple API for now, as that provided the data that I needed for the original 'itch' that I was scratching. The itch was to include video content on a personal website, displaying thumbnails for the available videos and having it fit with the existing look of that site. In writing some test/example code for it, it dawned on me that this might be a good fit for something else I've been meaning to test - testing to see that adding a completely new gallery style for Ansel would work as expected.

Ansel supports different gallery styles - you can have your photo galleries look like Polaroid images, view them in a Lightbox, etc.. My eventual goal with that feature is to not only allow the existing styles to be tweaked, but to make it "easy" to add a custom style that might require it's own custom php code. Creating a flickr-like style, for example, would require not only a different layout, but would almost certainly require some custom javascript and the php code to generate it.

So, back to the Vimeo code - the site that I'm displaying videos on also has photo galleries that are rendered via Ansel's api. I thought it would be an interesting exercise to see if I could extend Ansel easily to render the Vimeo videos as a new gallery style. In this case "easy" means getting it to work by only adding new gallery views and templates - without touching any existing Ansel code. This would allow me to take advantage of Ansel's existing infrastructure to render the videos - with the same single line of code I'm using to render the photo galleries.

Turns out, it wasn't too difficult at all. I got the basic code to do it worked out in a few hours - it's still rough, but the basic premise is there and actually works! In fact, I think that it's a great example of how Ansel can be extended with gallery styles. So, with that in mind, let's walk through the process of what it took. Note that it's beyond the scope of this post to explain the inner workings of Ansel. Obviously some knowledge of Ansel's internals would be required to do something like this. I have provided a link to download the files that are required to do this for those that may be interested in tinkering.

First, a quick introduction to the Horde_Service_Vimeo class. It's still very new, and I would hesitate to even call it alpha code, so there are no guarantees that the interface won't change going forward. As it stands now, this is a simple example of how you would go about getting a list of videos for a particular user:

$params = array('http_client' => new Horde_Http_Client());
$v = Horde_Service_Vimeo::factory('Simple', $params);
$videos = $v->user('userid')->clips()->run();

That's it. The results are returned, by default, as a serialized PHP array so you would call unserialize() on the results to actually get at the data.

$videos = unserialize($v->user('userid')->clips()->run();

To get the necessary HTML to embed the actual video player on your site, you would use the following:

$results = $v->getEmbedJSON(array('url' => $thumb['url'])); 

This returns a JSON encoded object that can either be output directly on your page's javascript or unserialized in PHP and used there.  There are also other options you can pass to that method in the parameter array to control things like the embedded player's size and what text is or is not displayed over the still image when the video is not playing. There is a whole wealth of information available, and I point you to Vimeo's API documentation to see exactly what is returned and what the structure of the data is.

Moving on to Ansel now - each gallery style is implemented by both an Ansel_View_GalleryRenderer_* object in ansel/lib/Views/ and a template file for the style in ansel/templates/view/. For this example, I'm going to create a new Ansel_View_GalleryRenderer_GalleryVimeo class and a corresponding template file named galleryvimeo.inc. I used the GalleryLightbox renderer as a starting point, copied those files, ripped out the code regarding all the gallery actions since things like delete and move don't make sense for this type of view. I also decided, for simplicity, to use an overlay to play the videos instead of creating another view. I therefore included the javascript necessary to use the RedBox overlay.

The first thing I needed to figure out was how to populate the class' private members with the video thumbnails. Luckily, the parent class Ansel_View_GalleryRenderer uses a single method, fetchChildren(), to fetch the items that are to be displayed in this gallery. This method can be overridden in our class so we can download the data from Vimeo instead of from our internal storage. You can see the code used to do this in the example file, though it's not all that different from the example listed above.

$thumbs = unserialize(
    $this->_vimeo->user($vimeo_id)->clips()->run());
foreach ($thumbs as $thumb) { $this->_json[$thumb['clip_id']] = $this->_vimeo->getEmbedJSON( array('url' => $thumb['url'], 'byline' => 'false', 'portrait' => 'false')); }

 

In the sample file, I'm hard-coding my Vimeo user id. If this were to mature into an actual feature in Ansel, I would use a new user preference for storing this value. The other thing to notice is that the Simple API doesn't support any type of paging. You get all the videos that match your request - up to whatever maximum value Vimeo itself imposes, so we have to emulate paging in our overridden method. The code to do that is simple, using the per-page value, the current page number and then getting an appropriate slice of the video array we retrieved from Vimeo.

// Total number of thumbnails in the gallery
$this->numTiles = count($thumbs);
 
// The last one to display on this page
$this->pageend = min(
    $this->numTiles,
    $this->pagestart + $this->perpage - 1);

// The actual data for what is to be displayed on this page 
$this->children = array_slice($thumbs,                              
                              $this->pagestart - 1,
                              $this->perpage, true);

The other issue was how to actually display the image tiles. Ansel uses a separate class to represent image and gallery tiles, retrieving them via method calls on the Gallery and Image objects. For this, I just implemented another method in the GalleryRenderer, getTile() and use it in the template to generate the tile. All the information needed is present in the array returned from Vimeo, so it's extremely simple to build the tile.

Yes, there are still some things that don't quite work right - for instance, Ansel will happily allow you to upload images to this gallery - but will never show them, or let you delete them. Image counts for this gallery will still always show as zero when browsing your galleries since there are no images for this gallery in the database. Some of these issues could be fairly easily addressed either in the new renderer class, or more appropriately, by adding a new type of GalleryMode class - where all the image count calculations are normally done.

While not a traditional Ansel gallery, I feel this demonstrates what is possible with Ansel's gallery style architecture.  I'm actually currently using this on my family website to host videos of my daughter.  Future plans for Ansel do include native support for videos, but I think this is a great alternative in the mean time - and off loads some bandwidth to Vimeo to boot.  Who knows, maybe some type of remote gallery style will eventually make it's way into the official code base...

You can find the code for this at: http://theupstairsroom.com/downloads/vimeogallery.tgz