Video Encoding in the Cloud with AWS ElasticTranscoder: The Presentation

Back in July I wrote a bit about my implementation of AWS ElasticTranscoder within DetroitHockey.Net’s multimedia archives.  I knew as I was writing it that I wanted to take it a step further and I got the chance to do that last week, presenting an expanded version of my post as a lecture at TechSmith’s internal developer conference, TSCDevCon.

It was my first lecture-style presentation and it shows.  Lots of “ums” and “uhs” but I think it flows okay.  I did make the mistake of not checking my audio levels so it needs to be cranked up a bit.  I also didn’t realize we had some awesome mics recording the audience so I repeated questions for the benefit of the recording when I didn’t have to.  Apparently the screenshot slides weren’t as legible as I’d have liked.

Overall, it was a good learning experience for me.  I hope the attendees also learned something.  I look forward to getting the opportunity to try it again sometime.

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 = "1.0.0";

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 > cookies) {
				if (cookiesps > 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 > 0) {
					time = days + ':';

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

					time += ':';
				} else if (hours > 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 > 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 < 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 > cookies) {
		if (cookiesps > 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 > 0) {
		time = days + ':';

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

		time += ':';
	} else if (hours > 0) {
		time += hours + ':';
	}

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