The Problem:

You have video files being added to a fileserver, but you don’t know what’s in them and they’re stored remotely, so you can’t just download them to see what’s in them. You also need to share these files to the web, mobile, and desktop via automated transcoding.

The Solution:

First, set up your Mattermost server. If you’re running it on the same machine you’re hosting the webhook on you’ll need to modify this line the config.json file to include your local hostname:

"AllowedUntrustedInternalConnections": ""

Then, install the following items on your server.

  • ffmpeg
  • mediainfo
  • webhook
  • incron
  • RestClient Gem
  • Mediainfo

Next, configure your Mattermost server to allow incoming and outgoing web hooks. To do this, first go into the system console and enable incoming and outgoing web hooks. It’s under Settings -> Custom Integrations:

After that’s done, check out the project and set up the configuration for your video preview file. Make sure to name the file video_preview_config.yaml and put it in the same directory as the Ruby script.

Then, test the preview by running a command against an existing file. I’ve included a video file:

$ mattermost_video_preview.rb preview

You should see something like this in your Mattermost window:

Now that you’ve got it running, configure your webhooks to call the server when it’s run. Modify the file web hooks.example.conf to use the appropriate paths and then run this command to set it up:

# cp webhooks.example.conf /etc/webhooks.conf
# service webhooks restart

Finally, configure incron to watch the directory and trigger the script when it’s run. To do this, first add your username to the incrontab:

$ sudo echo $USER >> /etc/incron.allow

Then run incrontab -e to add the following rule:

/directory/to/watch IN_CREATE /path/to/mattermost-video-preview/mattermost-video-preview.rb preview $@/$#

And that’s it! Now, when a video file is placed in the watch directory it will generate a preview, and you’ll be able to click one of the buttons to run a command on the file.


This script is made up of two main functions: generate_previews and run_command As you can imagine, the first generates the preview images and posts it to the Mattermost channel, and the latter runs the command and sends updates to the Mattermost channel. The call_mattermost function is used to send information to the Mattermost incoming webhook, and the upload_file function moves the previewed image to the web server.


There’s a couple interesting things about this function. First is this bit, which calculates the screen grab interval based on the number of images needed and the duration of the clip

if framegrab_interval.to_i == 0
        total_images = 1
        framegrab_grid.split('x').each do |x|
            total_images *= x.to_i
        framegrab_interval = file_info.duration / total_images

Outputting information in a human-readable format is important, so there are two sections that generate human-compatible file sizes and durations. This is something I think I can refactor into a more general, possibly recursive function that will work for more than just file sizes and durations:

count = 0
    units = ['bytes', 'KB', 'MB', 'GB', 'TB']
    loop do
        break if filesize < 1024.0
        count += 1
        filesize /= 1024.0

    pretty_filesize = filesize.round(2).to_s + ' ' + units[count]

    duration = file_info.duration
    remainder = 0
    count = 0
    units = ['sec','min','h']
    loop do
        break if duration < 60
        count += 1
        remainder = duration % 60
        duration /= 60

    pretty_duration = duration.round(0).to_s + ' ' + units[count]

The message posted to Mattermost makes use of their Markdown-compatibility to present things in a nice table. It also iterates over the valid file operations, so you can configure different operations and have the included automatically.

message = "![#{base_filename}](#{uploaded_file_url})\n\n"
message+= "|#{base_filename}|[(preview)](#{uploaded_file_url})|\n"
message+= "|-|-:|\n"
message+= "|File Size| **#{pretty_filesize}**|\n"
message+= "|Duration| **#{pretty_duration}**|\n"
message+= "|Format| **#{file_info.format}**|"

actions = Config['FileOperations']
attachments_actions = []
actions.keys.each do |key|
    action_hash = {
        'name': key,
        'integration': {
            'url': [Config['Webhook']['url'], 'run-command'].join('/'),
            'context': {
                'command': key,
                'filename': File.realpath(filename)



This function is also pretty straightforward. First it checks to see that the user is passing a valid file operation, and then runs that command on the file. Because the file operations are stored in a config file you don’t have to worry about running unauthorized commands.

The operations can be configured with a few different options:

  • command: The command to run. You can specify the input and output filenames
  • text: Changes the default text that’s reported to Mattermost. Can include the input and output filenames in this as well
  • location: Changes the default location where the new file is stored


I wrote this to handle ripping DVDs, but anyone who’s handling a lot of video files can appreciate having relatively small previews of the videos for organizing them. Connecting them to Mattermost means you’re not having to manually watch a directory for new files, or move around large movie files just to see what’s in them.

If you have an application where users are uploading video you’re going to need to moderate them, or at least check out ones reported by other users, this could come in really handy. Putting the previews into Mattermost and giving the team there the ability to approve or deny them with a click would greatly speed up the moderation process.

Another use for this would be to curate videos generated by another department. For instance, your marketing department may have people generating video assets like screencasts that put the finished product in a specific folder. This script will generate a preview for it so you can see when it’s done and then run whatever commands you want.

Motherboard has a great article about Agility Robotics‘ Cassie. Cassie is a bipedal robot that’s currently available for purchase, but all it can do is walk around. Which leads Jordan Pearson to make this point:

A pair of working robot legs is great, but they won’t deliver a package containing your new pair of headphones on their own. For that, you need arms and eyes.

A robot pair of legs, while cool, isn’t going to take over the world. Autonomous systems like this are going to change things even more than the PC or the smart phone, and to succeed I think AR needs to get some inspiration from the PC industry: Standard connectors.

Standards are great

One thing that enabled the expansion of personal computers was creating standard connectors for peripherals. This allowed people to hook up all kinds of devices to their PCs, from scanners to cameras to hard drives to missile launchers. This increased the utility of PCs. It also meant that if your PC died or you wanted to buy a new one there was a pretty good chance you wouldn’t waste your investment in peripherals, and you could upgrade to new peripherals for a fraction of a cost of a new PC.

What this has to do with Cassie

Cassie is the equivalent of a really great peripheral. It’s good at one thing – walking – but without being able to do more it’s not much of a robot. Now imagine if there was a socket on top where you could put different attachments. Say a cargo pod with a camera that’s programmed to follow you around, like some drones are able to do now. You’d have your own personal shopping companion.

Potential ideas for attachments go far beyond cargo. Different arms, sensory, and navigation could be added to expand Cassie’s capabilities. And by opening up the platform to development they’ll allow other companies to build attachments. Amazon could build specialized picking/packing arms that could also work with their Kiva robots. Black and Decker could make a line of yard care tools that work with Cassie, allowing it to do everything from rake leaves to clean gutters to mop your floors.

Agility Robotics has a similar advantage to what Apple had back in the early 80s. They were the first to get into a massive market with a great product. Opening their platform to expansion would offload the work of all the other systems to other companies, so they can focus on making really great legs.

Update: Apple has released an update that fixes this bug. If you’re using OS X High Sierra you should update. If you’re not, keep reading because setting your root password is a good thing to do anyway.

What happened

Apple has a major security bug in the latest version of MacOS that allows anybody to get full admin (root) access without a password. As a result, people are a little peeved about it:

What to do about it

You can fix this even if you’re not using the latest version by setting your root password. First, open a Terminal window and enter this command:

sudo passwd -u root

This will ask you for your password, and then ask you to enter and confirm your new root password. Note: You don’t see what you’re typing or any asterisks when you enter passwords in the Terminal. Just relax and make sure you enter the password correctly:

Changing password for root.
New password:
Retype new password:

Finally, make sure to use a password that isn’t the same as your user account, and that’s either stored securely (I like LastPass) or is easy to remember but hard to guess.

If you aren’t an admin user you’ll see an error message like this:

USERNAME is not in the sudoers file. This incident will be reported.

Don’t freak out because the “reporting” is just making a note in a local file. (Or is it…) You won’t be able to fix this, but somebody who uses your computer must have an admin account, so ask them and they’ll be able to fix it. Or you can use the instructions on Apple’s site.

I expect Apple should have a fix for this soon, because it’s really bad.

You might not think it affects you, but the EU’s stringent privacy laws force multinationals to respect everyone’s privacy because it’s cheaper to build one system that’s EU compliant than one for each major legal regime. And the stiff punishments help, too.

I telecommute from a rural town and have one ISP to choose from. If you repeal net neutrality there will be nothing stopping them from blocking specific content, and to do that they’ll have to block VPNs as well. This is because VPNs would allow users to work around the content restrictions. Without the ability to VPN into my office I will not be able to work remotely, which would place a sever burden on my family as my home office is over two hours away and I would have to spend money commuting. I would also be away from my family much more.

My wife also owns a retail store with an online presence. Blocking access to customers would severely reduce her sales. Our margins are low, considering she sells used sporting goods, so she needs access to customers in order to stay profitable. Without this there’s a chance our small town will lose another business.

Please, we are small business owners and hard working tax payers. We need a free and open Internet to at least try for the American dream.