madlep.com

Julian Doherty. Coder. Ruby, Elixir, Javascript, Whatever.

Hitler Tries to Embed Subtitles With FFmpeg on OSX

Last weekend I made Hitler finds out about Operation Fortitude, a “Hitler Reacts” meme video in response to the bungled Operation Fortitude operation in Melbourne. Somehow the video went minorly viral, getting linked from a few news sources.

The effort to actually produce it though, was non-trivial. Being a command line geek, I figured there would be a way to do it using open source. There is: FFmpeg is the tool for the job. However on OSX, this isn’t so straight forward. In theory it should just work once you figure out the right incantation… But it doesn’t.

I got it all working in the end though, and figured I should write up the solution. Pat McKenna had a better idea on how to communicate it.

do it as a “Hitler tries to get ffmpeg to embed subtitles on OS X” video

For the traditionalists, here’s the actual commands to run

system config
1
2
3
4
brew install ffmpeg --with-libass --with-fontconfig --with-freetype
sudo vim /opt/X11/lib/X11/fontconfig/fonts.conf
  # add `<div>Library/Fonts</div>` as a `<fontconfig>` element in fonts.conf
echo "export FONTCONFIG_PATH=/opt/X11/lib/X11/fontconfig" >> .bash_profile

You can download the original video here, and the example .srt file from this video here.

generate quick preview video
1
2
3
4
5
# this generates a file with the subtitles embedded as a separate stream
# it only saves the text, and doesn't render it to a bitmap in the actual video frames
# so it won't show up when you upload to youtube
# but it runs instantly, so it's useful for previewing timing etc
ffmpeg -i downfall_bunker_scene.mp4 -i subtitles.srt -c:v copy -c:a copy -c:s mov_text preview.mp4
rendering subtitles
1
2
3
# this renders the actual file. On my 2013 Macbook Pro, it's about 3 minutes to run
ffmpeg -i subtitles.srt subtitles.ass # convert .srt subtitles to .ass format subtitle file
ffmpeg -i downfall_bunker_scene.mp4 -vf "ass=subtitles.ass" output.mp4 # render actual video

Happy memeing. Remember to always meme responsibly. Please be advised that the “Hitler Reacts” meme is severely past it’s use-by date, so only use when absolutely necessary (as an ironic response to a Godwin’s Law situation is OK).

Rack::Reloader Not Reloading? There’s `to_app` for That

I burnt a bunch of time over the last few days trying to get the Rack::Reloader middleware to… you know… reload stuff.

TL;DR – call to_app on your Rack::Builder block

I had a Rack app that was being mounted in a parent Rails app. Not wanting to stop/start Rails whenever I tweaked that little Rack app, I figured I’d try to get code in the app auto loading after each request. Tried (and failed) to do it as part of the parent Rails app, so I added use Rack::Reloader in to the middleware stack.

Because this is a mapped rack app, it didn’t have it’s own config.ru to set up the middleware. Instead, the Rack app that gets mounted in Rails routes.rb file is an instance of Rack::Builder. Something like this:

hello_rack.rb
1
2
3
4
5
class HelloRack
  def self.call(env)
    [200, {}, ["Hello, World"]]
  end
end
my_app.rb
1
2
3
4
5
6
7
require 'hello_rack'

# WARNING! This doesn't reload... don't copy+paste this as the solution. Read on for the answer!
MyApp = Rack::Builder.new do
  use Rack::Reloader
  run HelloRack
end

And then in our Rails routes file, we hook it all up

config/routes.rb
1
2
3
4
5
Rails.application.routes.draw do
  ...
  mount MyApp, at: "/my_app".
  ...
end

This all looks good, and when we change our code in hello_rack.rb it should pick it up, right?…

So I try it out. Then… nothing. No reloading… No reason why.

Time to crack open the Rack source code…

debugging

Later that day…

It turns out that using the Rack::Builder instance directly as the Rack app causes it to recreate the middleware stack every time a request is made – which calls .new on each middleware class defined.

Normally this is fine, but Rack::Reloader keeps state of what source files are loaded, and the last modified time of them. If the Rack::Reloader instance doesn’t know about the file, it records it’s current modified time, and moves on without reloading it – the assumption being that if the app has just started, then the file doesn’t need to be reloaded – because it’s only just been loaded.

Except it hasn’t just been loaded – Rack::Reloader only thinks it has, as it’s a brand new instance, and this is the first time it’s ever seen the file. By creating a new Rack::Reloader instance for every request, all the state about file modified time gets thrown away, and nothing ever gets reloaded.

The fix? #to_app

The fix for this is really easy. Just call Rack::Builder#to_app and use that instead of using the Rack::Builder instance directly.

#to_app generates the Rack app with all the mappings and middleware in place – which is what we want – but only once. Rack::Builder#call delegates to that every time causing brand new middelware to be instantiated for every request (I’m not sure if that is a bug or a feature…)

If we change our my_app.rb file to look like this: everything works beautifully.

my_app_with_proper_reloading.rb
1
2
3
4
5
6
require 'hello_rack'

MyApp = Rack::Builder.new do
  use Rack::Reloader
  run HelloRack
end.to_app # note `to_app` here - that's the secret sauce

Note that you won’t see this problem if you’re just writing a rackup file (i.e. config.ru) and using rackup to execute that – rackup calls Rack::Server.start – which does exactly this under the hood.

Programming – minutes of addictive joy from the flow of writing code, then hours of staring at the screen swearing trying to untangle which library is making everything behave weirdly.

Improving Envato Marketplaces Search - Synonyms & Related Items

Here’s another video of me at one of our internal envato tech demos.

From the forum thread discussing it:

Here’s a peek into our agile development process.

Once a fortnight we have an internal “demopalooza” to show off new features. These sessions are open to anyone at Envato to attend, so we try to keep them focused on “what is the value to users”, and avoid too much techie talk.

In this segment, I’m discussing new back end features the search team has been working on.

Envato Search Update Video

Here’s me in an update video by envato from October last year talking about some of the search engine work we’ve been doing on the team.

We’re finishing off a big project to consolidate search on the envato marketplace sites from a frankenstein mix of Sphinx, Solr, and an ancient version of elasticserach, onto a new platform built on an up to date elasticsearch infrastructure. Much nicer.

Using Stats to Not Break Search

At Envato (where I work) we’ve recently started a development blog called webuild.envato.com for everyone that helps build our various sites – developers, designers, product people, ops, whatever.

I wrote one of the launch articles: “Using Stats to Not Break Search”. It’s about the statistical approach we used in our work moving from Solr to elasticsearch to test that search relevancy hadn’t been broken.

How do you change around pretty much everything in your search backend, but still remain confident that nothing has broken? (at least not in a way you didn’t expect).

We can use statistics to do that. In particular, a technique called Spearman’s rank correlation coefficient. Lets have a look at it, and see how we can use it to compare search results before and after a change to make sure relevancy rankings haven’t gotten screwed up in the process.

Go and check it out

Baby Shows Why Gamification Is Bullshit

I’ve always been a little bit dubious of the whole “gamification” thing.

Ian Bogost’s piece on Gamification Is Bullshit talks about how gamification peddling consultants are pushing something of dubious worth. They’re doing this in order to make a buck from the enterprise suckers willing to buy it. This is true. But it’s also worth noting that the concept itself (as well as the marketing of it) is lacking in substance and basically just doesn’t work.

Gamification supposedly takes something that people aren’t interested in doing, wraps some game mechanics around it, and then it magically turns into something they do want to do. Giving you badges or achievements, or some other symbolic reward when you fill in your timesheet. It really doesn’t work though. It doesn’t make it any less brain numbing and unexciting just cause you “levelled up” while doing it.

Turns I’ve been unintentionally using gamification with my 15 month old son.

And it doesn’t work there either.

Like most little boys, he gets really excited about little things, and he also wants to imitate everything he sees adults do. One of these things has been putting his disposible nappies in the bin. He’d started to want to hold the nappy, and throw it in the bin. Then open the bin as well, then close it, then carry the nappy over from his change mat and do the whole thing.

It’s nice seeing him learn new skills. It also made him more interested in coming over laying still for his nappy change. While he didn’t hate this before, he definitely was bored with the whole thing, and it was a chore for him (if you’re reading this a decade or two from now Big Guy – no, dirty nappies aren’t that fun for me or your mother either ;) )

Great. As a parent, anything that means less work is all good. Encouraging kids to take responsibility and help out around the house and all that.

So we made it a game. We’d encourage him to pick up his nappy, and deposit it correctly, and then give him a big clap and lots of praise when he did. Which he absolutely loved and was so proud of himself. He clapped his hands and had a huge grin on his face. Very cute.

Then he mastered the game.

Suddenly he wasn’t interested in the chore of throwing out nappies anymore after that.

Really quickly, we’ve hit the bullshit in gamification (possibly babyshit in this case). Our “gamifying” of a menial task didn’t really make it any more appealing or interesting.

He was motivated in the first place because it was a good challenge. Something to learn. That is motivating. The praise and clapping and cheering was inconsequential. He would have done it anyway – and once the challenge wore off, he wasn’t interested anymore. And no amount of cheerleading from his parents is going to change that.

He already does a bunch of challenging activities on his own without any gamification. Stuff that would seem boring and repetitive to an adult, but it’s right at his level. He’ll find little projects to work on, like figuring out how to get his ride on truck up and down the stairs to the garden, or turning his legoman night-lite on and off over and over till he can hit the button with one finger with absolute precision. And once he masters these tasks, he really isn’t interested anymore. We can cheer and clap every time he does something, but if he’s not interested, he just won’t do it.

This is true of babies, and it’s true of adults. Shallow window dressing doesn’t make anyone any more motivated when it comes to sorting shit out. If something is interesting and/or we’re challenged by it, we enjoy it. If it’s dull monotony, we don’t.

Gamification != games

This is the same experience I’ve seen so many times with gamification in actual video games (you know, where gamification supposedly comes from, and where it’s supposedly such a wild success). The compelling part of a video game is the actual challenge and enjoyment of mastering the game itself. That is what the game is all about.

Gamification isn’t something inherent in video games. It emerged as a shallow design pattern in subscription MMO games to try and keep gamers engaged (and paying subscription money) long after the challenge and inherent motivation to play has worn off. The best way to do this is to add new content and challenges. But that’s expensive. Building a bunch of achievements to get more replay out of existing content is much cheaper.

But it doesn’t really work that well there either. So you went and patted every single character in the game world? It took you 3 weeks? Yeah. Me neither. Boring. I have better things to do with my time (like play other games that I haven’t mastered yet and still provide a challenge).

Gamification == bullshit

It’s the same thing with gamifying dull tasks as with slapping achievement in video games so you’ll play more once you’re reached boredom point. Just cause I get some badge to display on some profile, filling out timesheets is still a crappy task. My 15 month old son knows this, and once the novelty of gamification wears off, most people who have been gamified know this as well.

I’m really hoping this whole gamification thing goes away. I hear a lot of buzz from people implementing gamification features, and even more from consultants and “gamification experts” selling their services. I don’t really hear much excitement from people that actually have to use the gamified systems at the end of it all.

Yes, I use disposable nappies. Yes, that means I’m going to eco hell.

Note for any US readers: s/nappy/diaper/g

Malm. Super Simple Mail Server for Development

TL;DR

Malm is a super simple SMTP server/reader. It catches any message sent to it for any email address on any domain. But doesn’t forward anything on, and gives you a web view to look at what it’s caught. Point your local app’s SMTP config at it, and you’ll have a nicer time working with email sending code at development time.

1
2
gem install malm
malm # malm starts with SMTP running on port 2525, and web mail client on port 4567

Then set up your app to send SMTP messages to localhost on port 2525

Any email messages that your app sends out can then be viewed on http://localhost:4567

Coding email logic sucks

Email is a hassle. The code itself is fine, but the environment and tools around it are just extra friction that makes it harder to get things done.

Say I’ve just coded the sign in process for http://adopt-a-shaved-yak.example.com. I’m running locally on my dev box. It asks me to register. All the usual details. So I type in madlep@example.local for the email.

And then the app sends me a confirmation message.

Crap. I need to click on the link to activate the account, and I hadn’t set up a mail server running at example.local. The message went to a black hole. Lets spend 10 minutes getting a temp mail server running… And set up a host name pointing at example.local… And add a throw away account on the mail server… And then set up a login to that account on your mail client… Half an hour gone. I need another coffee.

Ok. Back from my coffee break. Where was I? Right, we’ll do the sign up again. Oh, now the app validation says I can’t use the same email address. Hmmm, let’s do the account setup routine again with a different username… 10 minutes later… Done. Now we can test the confirmation link. Awesome.

What’s next? What? Now I need to do something with accounts on different domains? You mean I’ll need to tweak host files so mail gets delivered to my test mail server?… GAH

By now it’s lunch time. I’m well caffeinated, but I haven’t gotten much done.

Malm is a better way

I don’t want to mess around with mail servers, and I get bored of mashing refresh in my mail client to look at what my app is sending out.

So this is where Malm comes in. It’s a simple gem I threw together to solve this problem. Just install the gem, start it up, and you’ve got a basic SMTP server your app can use. Plus it doubles as a web based email message viewer so you can easily view what your app has spammed to the world.

Malm uses GServer for talking SMTP, and Sinatra for the web front end. It’s all self contained with minimal dependencies and no configuration required.

You can even start it as a background daemon process if you want.

The web front end is still a bit rough, but is functional, and lets you see plaintext and HTML content for messages received.

It’s super useful combined with a Vagrant VM running the rest of your development environment. That’s how I’m using it.

And if it lets me avoid Outlook and/or Exchange… I’m happy.

Malming it up

Go get it

1
gem install malm

And run it

1
malm # malm starts with SMTP running on port 2525, and web mail client on port 4567

Or as a background daemon process

1
2
sudo -E malm -p 25 --start # starts on regular SMTP port 25, and as a background process
sudo -E malm --stop # stop it again later

Getting help

1
malm --help # what it says

Source code is on github for anyone interested.

What’s a Malm?

A bedroom drawer range from Ikea. It’s easy to setup, then you just chuck all your stuff at it, and it keeps it for you to do something with later. Kind of like Malm, the super simple mail server.

I was going to call this “mail me” or something like that, but thought that name might lead to confusion. So I just splatted the words together and said it fast, and I got “Malm”. I’d just assembled a couple of these babies for the room upstairs, so it was fresh in my mind and it stuck.