Turns out jQuery has the handy has selector for just this case.
Here’s a simple example that sets the title attribute of every <a> tag that contains an <img> tag and then attaches Fancybox, a lightbox-esque, image-enlargement plugin:
$("a:has(img)").attr("title", "Click to enlarge").fancybox();
I liked this approach because it meant I didn’t have to remember to add a class (e.g. class="fancybox") to every <a> tag that linked to an image I wanted to enlarge. In fact, I didn’t have to do anything special at all. Brilliant!
And then I realized I had a problem. In a few instances on my blog, I used an image (like a Firefox or Ubuntu logo) to link to an external website. Even though a:has(img) was awesome in its expressive simplicity, it turned out to be a little overzealous in those instances.
What I really wanted was something slightly more specific: the ability to attach Fancybox to <a> tags that only linked to images. But how could I determine if I was linking to an image or not? How about by the file extension.
As much as I use and enjoy services like YouTube, Vimeo, and Flickr, I’ve never been a big fan of entrusting my content to large, centralized, corporate services. Yes they’re here today, yes they seem stable and secure, but will they always be around? Even while they are still in business, I’ve never liked the idea that the content I create effectively lives on their site, so they end up profiting from my page views. And frankly, centralization like that is counter to the distributed architecture at the core of the internet.
That said, this is as much to aid my memory for any future web video projects as it is to assist anyone else in a similar endeavor. It condenses about 18 hours of experimentation, research, and testing into a set of simple, easy to follow steps.
Step 1: Make some video
The easiest way to do this is with a point-and-shoot digital camera. I admit that I know absolutely nothing about video-editing, and even less about doing so on Ubuntu. That is fodder for a future post, I suppose. But honestly, we’re talking about short videos for the web. Who cares about editing?
Since the audio quality from most point-and-shoot cameras is rather poor, most people tend to replace it with music if it’s not critical to the content of the video. I also know very little about audio editing, but I can coax Audacity into cutting an audio track down to the length of the video, and adding a fade-in and a fade-out. Once I’ve got a wav or mp3 that I want to use, here’s the command to merge the audio and the video:
Until all browsers support the new HTML5 <video> element, Flash Video (.flv) is the de facto standard for streaming video on the web. At this point it helps to know what dimensions to encode the video to. I discovered that both the width and height need to be multiples of 2, as this helps with the data compression. On my blog, video with a 4:3 aspect ratio (like TV) translates to pixel dimensions of 380×284. Here is the FFmpeg command to convert an mp4 video to a reasonably compressed flv video:
Several sources recommended the JW FLV Media Player, which looks a little homely at first, but turns out to be infinitely configurable, as well as skinnable. I wanted my video player to resemble the Vimeo-style controls as much as possible (overlayed, display on hover, etc) and sure enough they had a skin, Bekle, that looked just like it.
Minimally, the player requires that I host a single file, player.swf, and optionally a second swf file for the skin. The look and feel of the player can then be modified by many configuration options set via flashvars. One of the flashvars worth mentioning, config, makes it possible to bundle up a series of common flashvars settings into a simple XML format, so I don’t have to repeat settings that I plan to implement site-wide (like colors, the skin URL, etc).
The documentation recommends using v1.5 of the swfobject.js library to embed the Flash Video on the page, but I found that to be overkill for my purposes. I write most of my posts in straight HTML, so I’d prefer to limit the content of my posts to HTML, rather than mess with inline JavaScript. That and WordPress tends to choke on inline JavaScript in posts.
The real benefit of swfobject is its battle-tested cross-browser compatibility. By foregoing that route, I had to figure out what combination of <object>, <embed>, and <param> tags was the most compatible across the latest versions of the big 3: Firefox 3, Safari 4, and Internet Explorer 8. As it turns out Firefox and Safari were the most forgiving, and managed to load and play every combination of tags I threw at them. IE would only load the video if it had a param tag of type “movie”. Ohhhhk.
As an aside: after letting IE6 lie fallow for 5 years, and then reawakening after of furious advances made by Mozilla, Apple, Opera, Adobe, and now Google, Microsoft has yet to support the object tag standard as defined in the HTML 4.01 spec from 1999. Embarrassing.
Anyway, I’m not about to stoop to support IE’s backwardness in every post that I embed video, so I wrote a quick WordPress plugin that would inject the necessary movie param tag into the object tag for any IE user-agents.
<?php
/*
Plugin Name: Add movie param to object tags for IE
Description: This plugin gets the data attribute from an <code><object></code> tag and adds it in as a <code><param name="movie" value="[data]"/></code> for Internet Explorers
Author: Justin Watt
Version: 1
Author URI: http://justinsomnia.org
*/
add_filter("the_content", "add_movie_param_to_object_tags_for_ie");
function add_movie_param_to_object_tags_for_ie($content)
{
if (isset($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false)) {
return preg_replace("#(<object\s+[^>]*?data\s*=\s*('|\"))([^'\"]+)(.*?)(</object>)#si", "$1$3$4<param name=\"movie\" value=\"$3\"/>$5", $content);
} else {
return $content;
}
}
Essentially there are two ways to make a time-lapse video, regardless of the tool. I chose FFmpeg because it was easy to use, and available on Ubuntu.
From Video
With the first, you take an existing video, recorded at 25 (or 30) frames per second, and strip out all the frames except for one every second. This gives you an effective speedup of 25x. For example, by extracting one frame per second from a 60 minute video, you can drop the length to a much more watchable, entertaining, and web-friendly 2.4 minutes (not to mention a smaller, easier to download file size).
Here’s a bash script (timelapsify.sh) that uses FFmpeg to convert a normal video into a time-lapse video. It takes an input video filename and an output video filename as command line arguments:
The other way is to set up your camera so that it takes a photo every 1 or 2 seconds. Many of the popular Canon Powershot cameras can be customized with a non-destructive, non-permanent firmware enhancement called CHDK which you can use to run a custom intervalometer (like this one). Or your camera may offer this functionality natively.
Keep in mind that 2 minutes of video represents 3000 distinct photos at 25 frames/second. Your battery or your memory card may run out before you get that far, so if possible, plug your camera in and set it to its lowest resolution setting.
One FFmpeg-specific caveat: in order to assemble photos into a video, you need to name them sequentially, starting from 1. So I wrote a simple little command line PHP script to rename the series of image filenames that my camera produced (IMG_3550.JPG, IMG_3551.JPG, IMG_3552.JPG) into something a little more FFmpeg-friendly (frame_1.jpg, frame_2.jpg, frame_3.jpg):
And here’s the FFmpeg command to assemble all those renamed “frame” jpgs into an actual mp4, H.264-encoded video (very much like timelapsify.sh above):
Have you ever wanted to write a plugin that sits between a request for a WordPress page (or post) and the fulfillment of that request? In order to do so, use the pre_get_postsaction hook which passes a WP Query object as a parameter to your plugin function. This action is called early in the request flow, before any headers or content has been sent to the browser. So you can probe or modify the query object in order to create your own custom behavior.
As an example, I wanted to redirect all requests that come in for http://justinsomnia.org/random/ to a random post from my blog. Note the random link in my nav bar above. Here’s how you might do that:
<?php
/*
Plugin Name: Random Link
*/
add_action('pre_get_posts', 'random_link');
function random_link($query)
{
if ($query->get('pagename') == 'random') {
global $wpdb;
$sql = "select $wpdb->posts.ID
from $wpdb->posts
where post_password = ''
and post_type = 'post'
and post_status = 'publish'
order by rand()
limit 1";
$post_id = $wpdb->get_var($sql);
$permalink = get_permalink($post_id);
header("Location: " . $permalink, true, 302);
exit;
}
}
The result is a pretty serendipitous (and addictive) way to surf my archives.
One day I would like to write a post called “test-driven development for web applications”. So apologies to anyone who came here looking for that. Because I have no idea what that is. But maybe you do? Please feel free to leave a comment.
I would also like to write a tutorial called “how to write unit tests for web applications” because almost every example I’ve come across (and I admit, I have not looked hard) of how to write unit tests tends to involve obvious classes like BankAccount or abstract classes like ObjectFactory. Sheesh.
At work I help lead the development of a web application written in PHP on top of a MySQL database. More than a year ago we decided: “we need unit tests”. So we took the typical approach and used PHPUnit to generate a stub for each of our core “model” classes. Starting with the shortest and easiest classes I began writing tests. What a pain in the ass. The one good thing that came out of it is that I made it possible to do a single command install of our codebase.