Christmas Name Drawing Generator

Every year my in-laws do the whole “drawing names” thing at Christmas, where each person draws one other person and buys a gift for them.  Nearly as often, there’s drama about it.  Someone complains about “always” getting someone else.  Someone gets themself (that was me this year).

I decided to remove the human element from it and make a little tool that would do the draw for us.  As my wife said, “Just because you write an app doesn’t mean things are gonna change.”  It was a good little exercise, though.

To make it more of an exercise, I did the entire thing on the client-side.  I do enough server-side work that it would have been really easy to fall back on that and not learn anything.  It’s a little sloppy but it works.

On to the code…

<div id="name_list">
  <h3>Names</h3>

  <ul>
  </ul>
</div>

<h3>Add a New Name</h3>

<form action="#" id="name_form" method="get" class="general">
<div>
  <label for="new_name">New Name:</label>
  <input type="text" name="new_name" id="new_name" />
</div>
<div class="checkbox">
  <input type="checkbox" id="allow_self_selection" />
  <label for="allow_self_selection">Allow Self Selection?</label>
</div>
<div id="buttons">
  <input type="submit" id="add_name" value="Add" />
  <input type="button" id="generate" value="Generate" disabled="disabled" />
  <input type="button" id="clear_list" value="Clear List" disabled="disabled" />
</div>
</form>

I start with a div that contains a header and an empty list. This div is hidden on page load and will contain the list of names as they’re added. Then I’ve got a form for adding names to the list, clearing the list, generating the drawn list, and for determining whether or not we’re going to allow people to draw themselves. That’s it for the page content, everything else is JavaScript/jQuery.

var names = new Array();

$(document).ready(function() {
  $('div#name_list').hide();
});

$('form#name_form').submit(function(e) {
  e.preventDefault();
  add_name();
});

I start out by giving myself a global array called names to hold the drawn names. As previously mentioned, on page load I hide the div containing the names list. On submit of the form I run a function called add_name().

$('input#clear_list').click(function() {
  $('div#name_list ul').html('');
  $('div#name_list').hide();
  $('input#generate').attr('disabled', 'disabled');
  $('input#clear_list').attr('disabled', 'disabled');
  $('input#add_name').removeAttr('disabled');
});

To clear the name list I wipe out the contents of that particular UL and re-hide the div containing it. Then I make sure to disable the buttons for generating a list and clearing the list (since there’s no list to work with) and enable the ability to add names.

$('input#generate').click(function() {
  $('input#add_name').attr('disabled', 'disabled');
  $('input#generate').attr('disabled', 'disabled');
  $('input#clear_list').attr('disabled', 'disabled');

  populate_list();

  $('input#generate').removeAttr('disabled');
  $('input#clear_list').removeAttr('disabled');
});

To generate the list I have a populate_list() function wrapped in button enabling/disabling. All of the buttons are disabled, then populate_list() runs, then we re-enable the Generate and Clear List buttons.

function add_name () {
  var new_name = $('input#new_name').val().trim();
  if (new_name) {
    $('div#name_list').show();
    $('div#name_list ul').append('<li><span class="giver">' + new_name + '</span><span class="arrow"></span><span class="recipient"></span></li>');
  }

  if ($('div#name_list ul li span.giver').length > 0) {
    $('input#clear_list').removeAttr('disabled');
  }

  if ($('div#name_list ul li span.giver').length > 1) {
    $('input#generate').removeAttr('disabled');
  }

  $('input#new_name').val('');
}

Okay, some actual functionality. The add_name() function is what actually puts an entered name into the list. First we get the value of the new_name input field and trim it. If there’s actually something there, we show the name list container (in case it wasn’t already visible) and add this new name to the list as a list item, with some extra spans for structure. The name itself is in a span with the giver class while there are empty spans with classes of arrow and recipient.

Then I check the length of the name list. If there are any names (as there should be since we just added one), the ability to clear the list becomes available. If there’s more than one, the ability to generate a list from them is open. Then I clear out the contents of the new_name input field.

function populate_list () {
  names = new Array();
  $('div#name_list ul li span.giver').each(function() {
    names.push($(this).html().replace('<span class="giver">', '').replace('</span>', ''));
  });

  names = shuffle(names);

  $('div#name_list ul li span.giver').each(function() {
    var name = $(this).html().replace('<span class="giver">', '').replace('</span>', '');

    if ((name == names[0]) && (!$('input#allow_self_selection').is(':checked')) && (names.length == 1)) {
      populate_list();
    } else {
      do {
        names = shuffle(names);
      } while ((name == names[0]) && (!$('input#allow_self_selection').is(':checked')));

      $(this).parent().children('span.arrow').html('&raquo;');
      $(this).parent().children('span.recipient').html(names[0]);
      names.shift();
    }
  });
}

Now we’ve got the actual meat of the list generation, the populate_list() function. We start by blanking out the array of names (just in case), then we loop through the contents of the name list UL to re-populate it. Stripping out the span surrounding the name itself should work by doing $($(this).html()).text() but it didn’t for me so I brute forced it and just replaced the span tags with nothing. Then we shuffle that list with a function I grabbed from StackOverflow.

I loop through the UL a second time to assign a recipient to each giver. I strip out the offending HTML again (I could have pulled that into a function but didn’t out of laziness).

If the only name in the names array is the same as the one we’re working on and we’re not allowing names to select themselves, we’ve hit a problem. In that case, we throw the whole thing out and brute-force it, re-running the populate_list() function.

If that’s not the case, I shuffle the names array until either the first name in the array is not the one we’re currently drawing for or people are allowed to draw themselves. For aesthetics I pop an arrow in the arrow span for the LI we’re working on, then I put the first name from the names array in the recipient span. I wrap it up by shifting the array to get that name out of the way.

There’s more brute-force than I’d like, defaulting to shuffling whenever we find a conflict, but it seems like a real-world name list would be around ten people and I don’t see an issue with performance at that level. No, it’s not the best code but it does what it needs to do.

Cookie Clicker Countdown User Script

Over the past week or so I’ve been a little too obsessed with Cookie Clicker. It’s an awesome little JavaScript-based game and I can’t just leave it alone.

I’m at a point in the game where it takes hours before I can buy the next item I’m looking for.  Just out of curiousity, I was doing the math to figure out exactly how long it would take at my current rate to hit certain milestones.  Since I was doing that so much, I decided to build a little user script I could install that would do the math for me and keep a running countdown going.

The script is available for download but I’m going to break it down a bit (in the spirit of those code samples I’ve talked so much about over the last couple days) here.

// ==UserScript==
// @name          CookieClicker Target Countdown
// @version       1.0.0
// @namespace     http://www.clarkrasmussen.com/
// @description   Enter a target amount of banked cookies and see the amount of time it will take to reach that amount at your current CPS
// @include       http://orteil.dashnet.org/cookieclicker/*
//
// ==/UserScript==

const VERSION = &amp;quot;1.0.0&amp;quot;;

window.addEventListener('load', function() {
	exec(function () {

We start with some pretty standard header stuff. On page load we run the exec() function, passing into it a function that wraps all of the stuff to continue. The exec() function is last in my code so I’ll get to it near the bottom of this.

		// write out our elements
		var output_div = document.createElement('div');
		output_div.setAttribute('id', 'cookieclicker_countdown_container');
		output_div.setAttribute('style', 'position: fixed; right: 30px; bottom: 20px; margin: 0; padding: 7px 15px; border: 1px solid #999; background-color: #ccc; color: #000;');

		var countdown_target_label = document.createElement('label');
		countdown_target_label.setAttribute('style', 'display: block; padding-bottom: .25em; font-size: 80%; font-weight: bold;');
		countdown_target_label.setAttribute('for', 'countdown_target_input');
		countdown_target_label.innerHTML = 'Target Cookie Value:';

		var countdown_target_input = document.createElement('input');
		countdown_target_input.setAttribute('style', 'display: block; font-size: 90%;');
		countdown_target_input.setAttribute('id', 'countdown_target_input');
		countdown_target_input.setAttribute('value', '0');

		var output_content = document.createElement('p');
		output_content.setAttribute('style', 'padding-top: .5em; text-align: center;');
		output_content.innerHTML = '00:00';

		output_div.appendChild(countdown_target_label);
		output_div.appendChild(countdown_target_input);
		output_div.appendChild(output_content);
		document.body.appendChild(output_div);

Next we define all of the HTML elements we need to actually display our output and we append them to the page. This includes a form element which we’ll use to define our target value.

		// do the check every second
		setInterval(function () {
			countdown_check();
		}, 1000);

Simple enough, run the countdown_check() function once a second.

		function calculate_countdown (countdown_target) {
			var cookies = Game.cookies;
			var cookiesps = Game.cookiesPs;
			var seconds = 0;
			var time = '';

			if (countdown_target &amp;gt; cookies) {
				if (cookiesps &amp;gt; 0) seconds = (countdown_target - cookies) / cookiesps;

				var days = Math.floor(seconds / 86400);
				var hours = Math.floor((seconds % 86400) / 3600);
				var minutes = Math.floor(((seconds % 86400) % 3600) / 60);
				seconds = Math.round(((seconds % 86400) % 3600) % 60);

				if (days &amp;gt; 0) {
					time = days + ':';

					if (hours &amp;gt; 0) {
						time += zero_pad(hours, 2);
					} else {
					 	time += '00';
					}

					time += ':';
				} else if (hours &amp;gt; 0) {
					time += hours + ':';
				}

				time += zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2);
			} else {
				time = '00:00';
			}

			return time;
		}

In the calculate_countdown() function we get the actual time remaining to the target. We pass in the target value, then grab the amount of cookies the user has and their cookies per second from the Game object so we don’t have to get it out of any HTML elements. It’s already readily available, why not take advantage? Then we do the math to determine how many cookies we need to hit the target and how many seconds it will take to get there.

If the number of seconds is greater than zero, we do some math to break the time into days, hours, minutes and seconds. Then we reassemble that into a string. If the time has already passed, we return 00:00.

The zero_pad() function seen implemented here is defined below.

		function countdown_check () {
			var countdown_target = parseInt(document.getElementById('countdown_target_input').value);

			if (countdown_target &amp;gt; 0) {
				var time = calculate_countdown(countdown_target);
				output_content.innerHTML = time;
			}
		}

Here’s that countdown_check() function. It grabs the countdown target from the form element and, if it’s greater than zero, throws it off to calculate_countdown() to get the time. The time returned is then put into the output_content container.

		function zero_pad (num, length) {
			var s = num + '';
			while (s.length &amp;lt; length) s = '0' + s;
			return s;
		}

	});
});

function exec (fn) {
	var script = document.createElement('script');
	script.setAttribute('type', 'application/javascript');
	script.textContent = '(' + fn + ')();';
	document.body.appendChild(script);
	document.body.removeChild(script);
}

Last we define zero_pad() and close out the containers, as this has all been wrapped in AddEventListener() and exec(). The exec() function is then defined as what allows us to do this all via script injection, which is what gives us access to that helpful Game object.

Hardly anything groundbreaking, in fact I’d bet someone else has done this before.  Just another one of those things I thought I’d share.

For the record, this is what the countdown looks like on the page.  Small enough to not interfere with a golden (or red) cookie appearing in the same spot.

cookieclicker

Update – 10:20 PM, 10/1/2013: After some quick feedback I made two changes.  One extracted the time formatting logic into its own function, the aptly-named format_time(), while the other fixed a bug by moving the rounding in calculate_countdown().  Those two functions now look as follows:

function calculate_countdown (countdown_target) {
	var cookies = Game.cookies;
	var cookiesps = Game.cookiesPs;
	var seconds = 0;
	var time = '';

	if (countdown_target &amp;gt; cookies) {
		if (cookiesps &amp;gt; 0) seconds = Math.round((countdown_target - cookies) / cookiesps);

		var days = Math.floor(seconds / 86400);
		var hours = Math.floor((seconds % 86400) / 3600);
		var minutes = Math.floor(((seconds % 86400) % 3600) / 60);
		seconds = ((seconds % 86400) % 3600) % 60;

		time = format_time(days, hours, minutes, seconds);
	} else {
		time = '00:00';
	}

	return time;
}

function format_time (days, hours, minutes, seconds) {
	var time = '';

	if (days &amp;gt; 0) {
		time = days + ':';

		if (hours &amp;gt; 0) {
			time += zero_pad(hours, 2);
		} else {
			time += '00';
		}

		time += ':';
	} else if (hours &amp;gt; 0) {
		time += hours + ':';
	}

	return (time + zero_pad(minutes, 2) + ':' + zero_pad(seconds, 2));
}

Retro Code Sample: Opening Night Countdown Image

Sometime soon I’ll write a bit on my new-ish philosophy about code samples.  For now it’s enough to say that I’m cleaning out that section of my site but there’s one project I wanted to keep documented, so I’m writing it up here and now even though the code is from 2005.

The “Opening Night Countdown Image” was my first foray into using the PHP image functions and the GD2 library.  Based heavily on the code from the Church Sign Generator, it took the hard-coded date of the Detroit Red Wings’ first game back after the 2004-05 NHL lockout and generated an image displaying the number of days remaining until that date.

There were four parts to the project: Two resource image files in .gif format, the actual PHP that mashed those together properly, and a .htaccess file that allowed for a pretty URL for the image.

The .htaccess file was simple enough.  Inside a folder called /nhl_countdown/ it looked as follows:

# General rewrites
RewriteEngine on
Options +FollowSymlinks
RewriteBase /
RewriteRule ^.htaccess$ - [F]

# image rewrite
RewriteRule countdown.gif /nhl_countdown/image.php

Turn rewrites on, allow Symlinks (completely unnecessary), set the rewrite base, block the .htaccess file, and actually rewrite requests for countdown.gif (the pretty filename) to the actual PHP script that generated the image.

The resource images were also pretty simple.

background

numbers

The first was just a static background featuring the team logos and an empty space where the countdown numbers should be. The second was the set of numbers to use in the countdown itself.  Both were .gif for some reason, probably because of the lack of support for .png in 2005.

The code is where it gets fun so let’s get into that.

// FIND OUT THE TIME REMAINING
$date = '2005-10-05';  // SET THE DATE

$parts = explode('-', $date);

$zero_hour = mktime (0, 0, 0, $parts['1'], $parts['2'], $parts['0']);
$today = mktime (date('H'), date('i'), date('s'), date('m'), date('d'), date('Y'));

$difference = $zero_hour - $today;

$s = $difference;
$d = intval($s / 86400);
$s -= $d * 86400;
$h = intval($s / 3600);
$s -= $h * 3600;
$m = intval($s / 60);
$s -= $m * 60;

if ($d <= '0') {
	$days = '0';
} else {
	$days = $d + 1;
}

We first determine the number of days remaining until gameday. This is horribly inefficient but it’s easy to say that with eight more years of development experience. This isn’t a code review, it’s a sample, so I’m not going to correct myself.

/* This is the list of allowed characters. The reason more characters aren't allowed is because I didn't put more characters in the characters image. More characters means more figuring out coordinates, ie. more work. Each character array comprises the character itself, its X coordinate (the Y coordinate is always 0), and its width (the height is always 12, the height of the image). */

$allowed_chars = array(
	array('1', 0, 33),
	array('2', 33, 35),
	array('3', 68, 32),
	array('4', 100, 35),
	array('5', 135, 32),
	array('6', 167, 33),
	array('7', 200, 31),
	array('8', 231, 34),
	array('9', 265, 32),
	array('0', 297, 33),
);

Next we map out what parts of the number resource file relate to each actual number. This is another thing I’d probably do differently now.

/* load the characters image and the background image into memory */
$chars_img = imagecreatefromgif('numbers.gif');
$background_img = imagecreatefromgif('background.gif');

/* get the width and height of the background image */
$src_w = imagesx($background_img);
$src_h = imagesy($background_img);

/* create a new image, that will be output to the browser */
$output_img = imagecreatetruecolor($src_w, $src_h);

/* copy the background image onto the output image */
imagecopy($output_img, $background_img, 0, 0, 0, 0, $src_w, $src_h);

/* Since the letters are centered on the background, the x centerpoint gives a starting point from which the x offset for each letter can be calculated. The y offset is simply the distance from the top of the image to the top of the first row of letters. */
$x_center = 300;
$y_offset = 27;

Then we do a little setup, getting the image resources loaded in and getting ready to do our typesetting.

/* This calculates the x offset for the text. It loops through the letters of the text and adds the width of each letter to the x centerpoint to get the beginning point. */

$x_offset = 0;

// calculate offset
for ($i = 0; $i < strlen($days); $i++) {
	/* Get the current character */
	$curchar = substr($days, $i, 1);

	/* loop through the characters array until we reach the one matching the current character, and add half its width to the x offset. */
	foreach($allowed_chars as $char) {
		if ($curchar == $char[0]) {
			$x_offset += ceil($char[2] / 2);
		}
	}
}

for ($i = 0; $i < strlen($days); $i++) {
	/* The second pass actually copies each letter from the characters image onto the output image. */
	$curchar = substr($days, $i, 1);

	foreach($allowed_chars as $char) {
		if ($curchar == $char[0]) {
			/* The imagecopymerge() function copies a rectangular area from one image onto another image. This is documented more than adequately on php.net. */
			imagecopymerge($output_img, $chars_img, ($x_center - $x_offset), $y_offset, $char[1], 0, $char[2], 45, 100);
			$x_offset -= $char[2];
		}
	}
}

We loop through each digit in the number of days remaining twice. First to find the size of the image representing the character so we can properly center the completed text, then to actually add said text to the new image.

/* Now that the image is built, it gets sent to the browser. First, send out HTTP headers to tell the browser a GIF image is coming. */
header("Content-Type: image/gif");
header("Content-Disposition: inline; filename=countdown.gif");

/* The imagegif() function sends the output img to the browser. */
imagegif($output_img);

/* Housekeeping functions - destroy the scratch images to free up the memory they take up. */
imagedestroy($background_img);
imagedestroy($chars_img);
imagedestroy($output_img);

Then we actually output the new image and do some cleanup.

As I said, it’s old, horribly inefficient code. As I’m cleaning up my code samples, though, this is something I didn’t want to lose (perhaps as a reminder of how far I’ve come) so I thought it made sense to write up a post on it.

Video Encoding in the Cloud with ElasticTranscoder

I’ve had a “multimedia” section on DetroitHockey.Net since about the third day the site existed but I haven’t always done a good job keeping said multimedia in a usable format. For awhile all of the videos were in QuickTime format, then I jumped over to Windows Media.  There were whole seasons of hockey where I didn’t bother adding any new highlights to the site because first I couldn’t figure out what the best format to use would be, then I didn’t want to take the time to burn through the backlog of highlights I needed to edit, encode, upload, etc.

Dumping all of the videos off on to YouTube wouldn’t be an option for me because I try to own the entire end-to-end experience on DH.N.  I don’t want to dump people off on a third-party to get something that I am supposedly providing.

About 18 months ago I finally sat down and took on the challenge of bringing the multimedia system up to date.  I pulled up the raw capture files for every video I had, including my backlog.  I re-edited everything, re-watermarked it, and re-encoded it all in HTML5-friendly formats.  I did it all by hand because I wanted to keep an eye on things as they went along but the entire time I was thinking, “I need to automate this going forward.”

After the updated multimedia section launched I had the idea of setting up FFmpeg on my server and using it to do the encoding rolling around in my head for awhile.  The idea would be that I’d upload the edited video and have automated processes that encoded it to the right formats, added the watermark, and copied everything to the right place.  I never got around to that before Amazon Web Services put out their ElasticTranscoder service.

Put simply, ETC does what I wanted to use FFmpeg for.  Here’s a bit on how I’m now using it.  All code shown is PHP (as should be evident) using AWSSDKforPHP2.

There are three important concepts to using ETC.  The “job” defines what file is to be encoded and what preset(s) to use for the encoding.  The “preset” is the set of encoding parameters, including codec options. The “pipeline” defines where your files come from and what gets notified upon various job-related events, including completion and error.

There’s one caveat to that: watermarking.  Watermarks are defined in the job but the preset must be set up to allow for watermarks.  I Tweeted that I think it should only be on the job level or should be a fully-defined fourth concept (along with the pipeline, preset and job) that gets attached to a job, rather than splitting the definition across the preset and the job.  That said, the way I ended up implementing things it doesn’t matter.

The pipeline is the one constant for every DH.N encoding job.  I dump the edited video file into a specified S3 bucket (all input files for ETC must be in an S3 bucket) and the job puts the completed files back in that bucket.  Upon completion or error, ETC sends a message to a topic in the AWS Simple Notification Service.  I have an HTTP endpoint subscribed to that topic, where code runs to shuffle the completed files into their final locations. I should probably be using an SQS subscription instead of an HTTP one to reduce the possibility of data loss but I’m not right now.

So dump the file to S3 and kick off the job, right?  I’ve skipped setting up the preset and here’s why:

Presets in ETC can’t be edited.  Once you create one, that’s it.  So I could create a set of presets that work for what I need now and save all of their IDs for reference by my scripts but if I ever needed to change that preset I would have to create a whole new preset and then update my script with the new ID.  Not hard but it felt like an easy point to make a mistake.

Instead, since I’m programmatically firing off the job anyway via the AWS API, I fire off the command to create a new set of presets for each job first.  This limits me because you can only have 50 custom presets but I clean up after each job so it really just means I can only have a certain number of jobs active at a time.

Enough of my babbling, on to the code:

$preset = array();

// WATERMARKS AND THUMBNAILS ARE USED FOR ALL PRESETS, DEFINE THEM HERE
$watermark_args = array();
$watermark_args['Id'] = 'watermark';
$watermark_args['MaxWidth'] = '25%';
$watermark_args['MaxHeight'] = '25%';
$watermark_args['SizingPolicy'] = 'ShrinkToFit';
$watermark_args['HorizontalAlign'] = 'Right';
$watermark_args['HorizontalOffset'] = '5%';
$watermark_args['VerticalAlign'] = 'Bottom';
$watermark_args['VerticalOffset'] = '5%';
$watermark_args['Opacity'] = '35';
$watermark_args['Target'] = 'Content';

$thumbnail_args = array();
$thumbnail_args['Format'] = 'png';
$thumbnail_args['Interval'] = '60';
$thumbnail_args['MaxWidth'] = 'auto';
$thumbnail_args['MaxHeight'] = 'auto';
$thumbnail_args['SizingPolicy'] = 'Keep';
$thumbnail_args['PaddingPolicy'] = 'NoPad';

// CREATE MP4 PRESET
$args = array();
$args['Name'] = 'MP4 ' . date('n/j/Y - g:i A');
$args['Description'] = 'MP4 output for ' . $file_name;
$args['Container'] = 'mp4';
$args['Video']['Codec'] = 'H.264';
$args['Video']['CodecOptions']['MaxReferenceFrames'] = '3';
$args['Video']['CodecOptions']['Profile'] = 'baseline';
$args['Video']['CodecOptions']['Level'] = '4';
$args['Video']['KeyframesMaxDist'] = '90';
$args['Video']['FixedGOP'] = 'false';
$args['Video']['BitRate'] = ($use_hd_video) ? '2000' : '768';
$args['Video']['FrameRate'] = '29.97';
$args['Video']['MaxWidth'] = 'auto';
$args['Video']['MaxHeight'] = 'auto';
$args['Video']['SizingPolicy'] = 'Keep';
$args['Video']['PaddingPolicy'] = 'NoPad';
$args['Video']['DisplayAspectRatio'] = 'auto';

if ($use_watermark) {
  $args['Video']['Watermarks'][] = $watermark_args;
}

$args['Audio']['Codec'] = 'AAC';
$args['Audio']['SampleRate'] = '44100';
$args['Audio']['BitRate'] = '128';
$args['Audio']['Channels'] = '2';

$args['Thumbnails'] = $thumbnail_args;

$data = $etc->createPreset($args);
$preset['mp4'] = $data['Preset']['Id'];

As the comment says, the watermark and thumbnail-creation data is the same for all output file formats so I define that first.  You’ll notice that the actual watermark file isn’t defined there, that’s defined at the job level, which is one of the things I think is weird about how watermarks are handled.

Then I create my MP4 preset, defining all of the codec options and other variables.  The big thing here is that I’m using a higher bitrate for HD video than SD, so I define that on the fly.  I’m sure I could do more fine-tuning but I’ve forgotten more about video codecs than Brian Winn at MSU would like for me to admit.

With the preset definition built, I fire off the createPreset command, which spits back a ton of data including the newly-created presetID.  I save that ID for later.

I also create a WebM preset but I’ll save space and not include that here since it looks almost the same.

With the presets defined, it’s time to fire off the job.

// CREATE JOB
$args = array();
$args['PipelineId'] = $pipeline_id;
$args['Input']['Key'] = $source_file;
$args['Input']['FrameRate'] = 'auto';
$args['Input']['Resolution'] = 'auto';
$args['Input']['AspectRatio'] = 'auto';
$args['Input']['Interlaced'] = 'auto';
$args['Input']['Container'] = 'auto';
$args['OutputKeyPrefix'] = $output_folder;

foreach ($preset AS $type => $preset_id) {
  $output_args = array('Key' => (substr($source_file, 0, strpos($source_file, '.')) . '.' . $type), 'PresetId' => $preset_id, 'ThumbnailPattern' => '', 'Rotate' => 'auto');

  if ($use_watermark) {
    $output_args['Watermarks'][] = array('InputKey' => '_assets/watermark.png', 'PresetWatermarkId' => 'watermark');
  }

  $args['Outputs'][] = $output_args;
}

$data = $etc->createJob($args);

The pipeline ID is saved off in a configuration file, since that’s used for every job.  The source file and the output folder are defined in code outside this.  We loop through each of the previously-defined presets to say how that preset will be used (for example, I don’t use thumbnails on any of my jobs, even though they were defined at the preset level).  If I’m using a watermark, the watermark file is added to the job definition.  With the job defined, the createJob command is fired.

The job runs along in the background and I don’t care about the status, because I’ll know when it ends because my HTTP endpoint will be hit.  The endpoint looks like this:

$preset = array();
$path = array();

if ($data->state == 'COMPLETED') {
  if ($data->output) {
    $preset[] = $data->output->presetId;
    $path[substr($data->output->key, ((strpos(strrev($data->output->key), '.')) * -1))] = $data->output->key;
  } elseif (is_array($data->outputs)) {
    foreach ($data->outputs AS $output_data) {
      $preset[] = $output_data->presetId;
      $path[substr($output_data->key, ((strpos(strrev($output_data->key), '.')) * -1))] = $output_data->key;
    }
  }

  // SHUFFLE FILES AROUND
  $s3 = AwsS3S3Client::factory(array('key' => $config['access_key'], 'secret' => $config['secret_key'], 'region' => $config['region']));

  $s3->copyObject(array('Bucket' => $bucket, 'Key' => $file_location['mp4'], 'CopySource' => ($bucket . '/' . $output_folder . $path['mp4']), 'ACL' => 'public-read'));
  $s3->copyObject(array('Bucket' => $bucket, 'Key' => $file_location['webm'] . 'file.webm'), 'CopySource' => ($bucket . '/' . $output_folder . $path['webm']), 'ACL' => 'public-read'));

  $s3->deleteObject(array('Bucket' => $bucket, 'Key' => ($output_folder . $path['mp4'])));
  $s3->deleteObject(array('Bucket' => $bucket, 'Key' => ($output_folder . $path['webm'])));
  $s3->deleteObject(array('Bucket' => $bucket, 'Key' => $source_file));

  unset($s3);
} else {
  if ($data->output) {
    $preset[] = $data->output->presetId;
  } elseif (is_array($data->outputs)) {
    $preset[] = $output_data->presetId;
  }
}

// REMOVE JOB PRESETS
$preset = array_unique($preset);
if (count($preset)) {
  $etc =  AwsElasticTranscoderElasticTranscoderClient::factory(array('key' => $config['access_key'], 'secret' => $config['secret_key'], 'region' => $config['region']));

  foreach ($preset AS $id) {
    $etc->deletePreset(array('Id' => $id));
  }

  unset($etc);
}

We start by determining whether or not the job is complete. Because we’re only notified upon completion or error, we know that anything that isn’t completion is a failure.

On completion we loop through the data provided about the completed job to determine what presets were used and what files were created.  We move the new files to their final locations (and make them publicly readable) and remove the outputted files and the original input file.  On error we just determine what presets were used (we don’t delete any files in case they can be re-used).  In both cases, we then remove the presets that were used so that we don’t hit that 50-preset limit.

There are some other pieces that manage metadata and connections to other parts of DH.N but these are the interactions with ElasticTranscode.

PHP and the Trello API

A couple weeks ago I wrote up a bit about a PHP wrapper object I’d written for the Twitter API v1.1.  Since then I’ve been playing with the Trello API a bit, so I figured I’d write that up as well.

The code here is going to look really familiar because I actually wrote my Trello API wrapper object first, so the Twitter one is based on it.  Take a look…

<?php
  class trello_api {
    private $key;
    private $secret;
    private $token;

    public function __construct ($key, $secret, $token) {
      $this->key = $key;
      $this->secret = $secret;
      $this->token = $token;
    }

    public function request ($type, $request, $args = false) {
      if (!$args) {
        $args = array();
      } elseif (!is_array($args)) {
        $args = array($args);
      }

      if (strstr($request, '?')) {
        $url = 'https://api.trello.com' . $request . '&key=' . $this->key . '&token=' . $this->token;
      } else {
        $url = 'https://api.trello.com' . $request . '?key=' . $this->key . '&token=' . $this->token;
      }

      $c = curl_init();
      curl_setopt($c, CURLOPT_HEADER, 0);
      curl_setopt($c, CURLOPT_VERBOSE, 0);
      curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($c, CURLOPT_URL, $url);

      if (count($args)) curl_setopt($c, CURLOPT_POSTFIELDS , http_build_query($args));

      switch ($type) {
        case 'POST':
          curl_setopt($c, CURLOPT_POST, 1);
          break;
        case 'GET':
          curl_setopt($c, CURLOPT_HTTPGET, 1);
          break;
        default:
          curl_setopt($c, CURLOPT_CUSTOMREQUEST, $type);
      }

      $data = curl_exec($c);
      curl_close($c);

      return json_decode($data);
    }
  }
?>

I’ll admit right off the bat that the API secret is really only included for futureproofing.  As of right now, you only need it in order to get the API token, which you can then combine with the API key to make any calls you need.  The constructor could very easily only accept the key and token.

Say you wanted to use this to get the name of your board’s red label. That would look like this:

$trello = new trello_api($key, $secret, $token);

$data = $trello->request('GET', ('/1/boards/' . $board_id . '?fields=labelNames'));
echo $data->labelNames->red;

unset($trello);

We use the request method make a GET call to /1/boards/xxxxxx, where xxxxxx is the ID of the board we want the data for. We include the optional fields=labelNames query because the names of the labels are all the data we want to get back in this case. The request gives us back an object where each label’s name is available.

For the record, I kind of hate that Trello uses the color of each label as an identifier. I’m sure they can back the decision up but it reminds me of the old CSS “rule” about not naming your classes after what they look like, because what happens if you change what they look like?  If you have a class that converts everything to uppercase and bolds it, and you call it uppercase_bold, that works great until you change it to small caps and italics.  That’s a whole other post, I suppose.

Now that we have the text for the red label on our board, say we want to change it. That looks like this:

$trello = new trello_api($key, $secret, $token);

$trello->request('PUT', ('/1/boards/' . $board_id . '/labelNames/red'), array('value' => $text));

unset($trello);

This time it’s a PUT request to /1/boards/xxxxxx/labelNames/red (where, once again, xxxxxx is the board’s ID).  We use the optional $args argument of the request method to pass in a value of $text, where $text is the new label text.

What if you want to use that newly-renamed red label and apply it to a new card? That’s just a POST request to /1/cards.

$trello = new trello_api($key, $secret, $token);

$trello->request('POST', '/1/cards', array('name' => $card_name, 'idList' => $list_id, 'labels' => 'red'));

unset($trello);

We use the optional $args argument again to pass in our array of arguments. We set the name to whatever the value of $card_name is,  idList is set to $list_id (the ID of the list the card will be added to), and the optional labels specifies that we’re applying just the red label to this new card.

Again, I could complain about using the color as an identifier but whatever.

Like my work with the Twitter API, this is hardly groundbreaking stuff.  Just another thing I thought might be useful for people other than myself so I wrote it up.

PHP and the Twitter API v1.1

I’d been using the relatively-awesome TwitterOAuth library by Abraham Williams for quite some time to handle interactions between my sites and Twitter’s REST API v1.  With Twitter having eliminated v1 of the API, I started looking into other options.

It’s true that TwitterOAuth can be updated easily, changing a hardcoded 1 to 1.1 but Twitter introduced a new wrinkle with the move to v1.1 that v1 didn’t have: All requests must be authenticated.

This makes sense for actions such as posting a new Tweet, as you can’t very well do so without having a user to Tweet on the behalf of.  For that reason, you had to be authenticated to make that request in v1, so it’s nothing new for v1.1.  But what about if you just want to get the timeline of a specific user, or data on a specific Tweet?  Those are actions you might want to do through an automated process, in which case there would be no logged-in user to act on the behalf of.

Well, that’s what bearer tokens are for.  And TwitterOAuth doesn’t handle them.  So rather than use TwitterOAuth for one set of requests and something else for others, I wrote a new class that can handle Basic, OAuth, and Bearer auth types.

<?php
  class twitter_api {
    private $application_key;
    private $application_secret;
    private $user_token;
    private $user_secret;
    private $bearer_token;
    private $auth_type;
    private $oauth;
    private $args;

    public function __construct ($application_key, $application_secret) {
      $this->application_key = $application_key;
      $this->application_secret = $application_secret;
      $this->auth_type = AUTH_TYPE_BASIC;
    }

    public function auth ($type, $token, $secret = false) {
      if ($type == AUTH_TYPE_OAUTH) {
        $this->user_token = $token;
        $this->user_secret = $secret;
        $this->oauth = array('oauth_consumer_key' => $this->application_key, 'oauth_nonce' => (string)mt_rand(), 'oauth_signature_method' => 'HMAC-SHA1', 'oauth_token' => $this->user_token, 'oauth_timestamp' => time(), 'oauth_version' => '1.0');
      } elseif ($type == AUTH_TYPE_BEARER) {
        $this->bearer_token = $token;
      }

      $this->set_auth_type($type);
    }

    public function set_auth_type ($type) {
      $this->auth_type = $type;
    }

    public function request ($type, $request, $args = false, $body = false) {
      $this->args = (is_array($args)) ? $args : array($args);
      $full_url = $base_url = 'https://api.twitter.com/' . $request;

      if (($type == 'GET') AND (count($this->args))) $full_url .= '?' . http_build_query($this->args);

      if ($this->auth_type == AUTH_TYPE_OAUTH) {
        $this->oauth['oauth_signature'] = base64_encode(hash_hmac('sha1', $this->build_oauth_base_string($type, $base_url), (rawurlencode($this->application_secret) . '&' . rawurlencode($this->user_secret)), true));
        $header = array($this->build_oauth_header(), 'Expect:');
      } elseif ($this->auth_type == AUTH_TYPE_BEARER) {
        $header = array('Authorization: Bearer ' . $this->bearer_token);
      } else {
        $header = array('Authorization: Basic ' . base64_encode($this->application_key . ':' . $this->application_secret));
      }

      $c = curl_init();
      curl_setopt($c, CURLOPT_HTTPHEADER, $header);
      curl_setopt($c, CURLOPT_HEADER, 0);
      curl_setopt($c, CURLOPT_VERBOSE, 0);
      curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 1);
      curl_setopt($c, CURLOPT_URL, $full_url);

      switch ($type) {
        case 'POST':
          curl_setopt($c, CURLOPT_POST, 1);
          break;
        case 'GET':
          curl_setopt($c, CURLOPT_HTTPGET, 1);
          break;
        default:
          curl_setopt($c, CURLOPT_CUSTOMREQUEST, $type);
      }

      if ($body) {
        curl_setopt($c, CURLOPT_POSTFIELDS, $body);
      } elseif (($type != 'GET') AND (count($this->args))) {
        curl_setopt($c, CURLOPT_POSTFIELDS, http_build_query($this->args));
      }

      $data = curl_exec($c);
      curl_close($c);

      return json_decode($data);
    }

    private function build_oauth_base_string ($type, $url) {
      $incoming = array_merge($this->oauth, $this->args);
      ksort($incoming);

      $data = array();
      foreach ($incoming AS $key => $val) {
        $data[] = $key . '=' . rawurlencode($val);
      }

      return $type . '&' . rawurlencode($url) . '&' . rawurlencode(implode('&', $data));
    }

    private function build_oauth_header () {
      ksort($this->oauth);

      $data = array();
      foreach ($this->oauth as $key => $val) {
        $data[] = $key . '="' . rawurlencode($val) . '"';
      }

      return 'Authorization: OAuth ' . implode(', ', $data);
    }
  }
?>

You’ll never hear me say that this is some kind of end-all, be-all solution.  I’m not even sure it’s all that good.  It just appears to solve the problem I was trying to handle and since I didn’t see a lot of code that did, I figured it might be useful to post.

Some more details on how this works…

This is built to handle the kind of Basic auth requests you would need to make in order to get an OAuth or Bearer token to continue making requests.  After you get your bearer token, you can switch to using that.  Then you could switch to OAuth or back to Basic if you needed to.

Here’s an example:

$twitter = new twitter_api($consumer_key, $consumer_secret);

$data = $twitter->request('POST', 'oauth2/token', false, 'grant_type=client_credentials');
$bearer_token = $data->access_token;

$twitter->auth(AUTH_TYPE_BEARER, $bearer_token);
$data = $twitter->request('GET', '1.1/statuses/show.json', array('id'=> '345219659211108353', 'include_entities' => true));

print_r($data);

$twitter->set_auth_type(AUTH_TYPE_BASIC);
$data = $twitter->request('POST', 'oauth2/invalidate_token', false, ('access_token=' . $bearer_token));

unset($twitter);

We start by initiating the object using our application’s consumer key and consumer secret (no getting around that).  Because that’s all we have at that point, we use them with Basic auth to make the request for a bearer token.  That request is a POST to oauth2/token with a body of grant_type=client_credentials.  The third parameter of my request function is for any arguments for the API call and we have none for this one, so it’s set to false.

That request spits back an object that includes a bearer token, so we save that as $bearer_token for future use.

Our next request is for data on a specific Tweet.  We need OAuth or Bearer auth for that so we use the auth function to feed in the bearer token we just got.  That function will also switch us over to using Bearer auth for all of our subsequent requests.  With that out of the way, we use request again, this time hitting 1.1/statuses/show.json with a GET request.  Unlike in our previous call, we have optional parameters to use (but no body, so it can be ignored).  Our parameters will be passed in as an array, with the Tweet ID defined and include_entities set to true.

That request will return the data for the Tweet we specified.  Since we’re not doing anything with it in this example, we just spit it back out on the screen.

Since this example is done, we close it out by invalidating the bearer token we just created.  You probably would actually want to save that token to reuse it within your application but we destroy it for example’s sake.  To do that, we use set_auth_type to switch back to Basic auth, then we POST to oauth2/invalidate_token with a body of access_token=XXXXXXX (where XXXXXXX is the bearer token we got earlier).

For the record, had we wanted to make a request that required a user’s OAuth authorization, it would have looked like this:

$twitter->auth(AUTH_TYPE_OAUTH, $token, $secret);
$response = $twitter->request('POST', '1.1/statuses/update.json', array('status' => $text));

Where $text is the text to be Tweeted, of course.

As I said, this isn’t any kind of end-all, be-all.  It doesn’t have any kind of error handling, I’ve only tested it on the things I was already using the Twitter API for, and I’ve only tested it on my own machines.  It works for me, though, so I figured I’d throw it out to the world in case it might work for someone else.

S/T: There’s a great answer on StackOverflow about manually building the OAuth headers that really helped me out in this.

Output From Snagit to AWS S3 via S3 Browser

This is something I put together and never wrote up, so I might as well do so now.

Snagit by TechSmith is one of my favorite programs; I love being able to share what I’m seeing on my screen with collaborators and clients for easy comparisons with what they’re seeing.

With Snagit, there are several built-in output methods. Until recently, I mostly exported my screenshots to TechSmith’s Screencast.com service or to an online filebox I had built for myself on ClarkRasmussen.com via the built-in FTP functions. When I updated my filebox to use the Amazon Web Services S3 service, though, I lost the ability to upload files there straight from Snagit.

There is no Snagit to S3 output available but I found a way to do it via a Snagit program output and the S3 Browser program.

Snagit allows for outputs via the command line and S3 Browser has a command line mode, so the two can combine to allow you to upload files.

Let’s say you have an account in S3 Browser called myaccount and under that, you had a bucket called myfiles.domain.com. For added complexity you have a folder in that bucket called snagit where you want your outputted screenshots to live (though this added step can be skipped if you want to save straight to the bucket root). In that case, you can set up a Snagit program output with the following settings:

snagit-output1

When you share using the S3 Browser Console Uploader output, your screenshot goes straight into your S3 bucket and you can share the URL as necessary.

That’s useful but I think it’s missing a step. When you share to Screencast.com or via FTP, the file’s new URL is copied to your clipboard, so you can just paste the new URL into whatever communication medium you’re using. That doesn’t happen here, you have to manually type out the new URL, but it can with another intermediary step.

I wrote a batch script that acts as a go-between for Snagit and S3 Browser. It takes in an additional parameter and uses that to assemble the new file’s URL, then copies that to the clipboard. The batch script looks as follows:

@ECHO OFF
s3browser-put.exe "%1" "%2" "%3"

set baseurl=%4
set filepath=%2

for /F %%i in ("%filepath%") do set filename=%%~ni%%~xi

echo %baseurl%%filename% | clip
:END

Disclaimer: I hadn’t written a batch script in ages prior to this, so this can probably be cleaned up a bit.

I save that script as snagit-s3browser.bat inside the S3 Browser root folder. My updated Snagit program output to take advantage of it looks as follows:

snagit-output2

Since it’s truncated in the screen capture, the full Parameters line is as follows:

myaccount "<CaptureFilename>" myfiles.domain.com/snagit http://myfiles.domain.com/snagit/

This is similar to the first one except we’re pointing at the batch file rather than the S3 Browser command line utility directly. We’ve also added a fourth parameter that contains the base URL for your capture location, which maps to the bucket/folder combination used in the third parameter.

There’s no visual confirmation with this output but now when your share is complete, the file will have been uploaded and the new URL will be on your clipboard.

There might be a better way to do this but it’s been working pretty well for me. I’d love to hear if there are others solving the same problem differently.