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": "192.168.1.100 127.0.0.1"

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 clouds.mov

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 [email protected]/$#

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.

Discussion

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.

generate_previews

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
        end
        framegrab_interval = file_info.duration / total_images
end

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
    end

    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
    end

    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)
            }
        }
    }

    attachments_actions.push(action_hash)
end

run_command

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

Applications

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.

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:


Password:
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.

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.