5 Automation examples
Moritz Bunkus edited this page 2019-01-24 20:35:51 +00:00

Examples of how to automate things

[[TOC]]

MKVToolNix contains several command line tools that are designed to be scriptable. You can automate pretty much any type of processing when you combine those tools with a scripting language of your choice.

This page lists a couple of examples of how to do this. Most of them are written in the scripting language Ruby which is available for all operating systems out there. The examples are simple enough that they can be translated easily into other scripting languages such as Python or Perl.

You can even do a lot of work with a shell such as bash or zsh (for Windows you can use the one from mingw or git). I highly recommend the jq utility for processing JSON from the shell.

In general automation boils down to the following steps:

  • loop over all affected files,
  • query mkvmerge about the content of the file (this will be output in JSON and can therefore be processed quite easily with any language),
  • analyze mkvmerge's output and build a new command line for the tool you want to work with (command line documentation: mkvmerge, mkvpropedit),
  • execute mkvmerge or mkvpropedit with that new command line.

mtxlib.rb — common Ruby routines

The following contains a couple of common routines used by the other examples. You'll have to save this content as a file called mtxlib.rb in the same directory as the other examples.

#!/usr/bin/env ruby

# version 2019-01-24-01

require "json"
require "tempfile"

def identify_file file_name
  Tempfile.create(["mkvmerge-arguments", ".json"], nil, :encoding => "utf-8") do |file|
    file.puts JSON.dump([ "--identification-format", "json", "--identify", file_name ])
    file.close

    return JSON.load(`mkvmerge @#{file.path}`)
  end
end

def multiplex_file arguments
  Tempfile.create(["mkvmerge-arguments", ".json"], nil, :encoding => "utf-8") do |file|
    file.puts JSON.dump(arguments)
    file.close

    system "mkvmerge @#{file.path}"
  end
end

def edit_file_properties arguments
  Tempfile.create(["mkvpropedit-arguments", ".json"], nil, :encoding => "utf-8") do |file|
    file.puts JSON.dump(arguments)
    file.close

    system "mkvpropedit @#{file.path}"
  end
end

Example 1: multiplex files, change audio language, remove subtitle track

Here's an example in Ruby that loops over all files in an input directory, sets the second audio track's language to ger (if there's one) and de-selects the third subtitle track (if there's one). It uses option files instead of listing all arguments on the command line in order to avoid character set issues and maximum command line length limits.

This example is written in Ruby and requires the mtxlib.rb file from above.

#!/usr/bin/env ruby
# coding: utf-8

require_relative "mtxlib"

input_dir  = "input"            # Mux all MKVs from this sub-directory…
output_dir = "output"           # …into this sub-directory

Dir.glob("#{input_dir}/*.mkv").each do |input_file|
  json = identify_file input_file

  tracks_by_type = Hash.new
  arguments = [
    "-o",
    "#{output_dir}/" + input_file.gsub(%r{.*[/\\]}, '')
  ]

  json["tracks"].each do |track|
    tracks_by_type[track["type"]] ||= 0
    tracks_by_type[track["type"]]  += 1

    if (track["type"] == "audio") && (tracks_by_type["audio"] == 2)
      arguments += [ "--language", "#{track["id"]}:ger" ]

    elsif (track["type"] == "subtitles") && (tracks_by_type["subtitles"] == 3)
      arguments += [ "--stracks", "!#{track["id"]}" ]

    end
  end

  arguments << input_file

  multiplex_file arguments
end

Example 2: remove 'default track' flag from all subtitle tracks

This example processes all files in a sub-directory. It uses mkvpropedit in order to clear the "default track" flag for all subtitle tracks that have the flag set.

This example is written in Ruby and requires the mtxlib.rb file from above.

#!/usr/bin/env ruby
# coding: utf-8

require_relative "mtxlib"

files_dir = "files"             # Process all MKVs in this sub-directory

Dir.glob("#{files_dir}/*.mkv").each do |file|
  json      = identify_file file
  arguments = [ ]

  json["tracks"].each do |track|
    if (track["type"] == "subtitles") and (track["properties"]["default_track"] == true)
      arguments += [ "--edit", "track:=#{track["properties"]["uid"]}", "--set", "flag-default=0" ]
    end
  end

  if !arguments.empty?
    puts "Editing #{file}"
    edit_file_properties [ file ] + arguments
  end
end

Example 3: set title to the name of the file

This example processes all files with the extension .mkv or .webm in the current directory. For each file it takes its name without the extension and sets that as the new title for the file.

This example is written in shell (bash or zsh) and requires the jq utility.

for FILE in *.mkv *.webm ; do
  TITLE="$(mkvmerge -J "$FILE" | jq -r '.container.properties.title // ""')"

  if [[ -z $TITLE ]]; then
    TITLE="${FILE##*/}"
    TITLE="${TITLE%.mkv}"
    TITLE="${TITLE%.webm}"

    mkvpropedit "$FILE" --set "title=$TITLE"
  fi
done

Example 4: combine MP4 files with similarly named subtitle files

Often you might have a lot of MP4 files lying around with subtitle files that are similarly named: only the extension differs (e.g. Wonderful Movie.mp4 and Wonderful Movie.srt). The following example iterates over all MP4 files in the current directory. It looks for SRT and VobSub subtitle files in the same directory that have the same base name (both are used if both are found). Then it multiplexes all of them together into a Matroska file.

This example is written in shell (bash or zsh) and doesn't have any other requirements.

# an array for the arguments
declare -a args

# loop over all MP4 files
for mp4 in *.mp4 ; do
  base="${mp4%mp4}"
  args=(-o "output/${base}mkv" "${mp4}")

  # look for subtitles with the same base name
  if [[ -f "${base}srt" ]]; then
    args=("${args[@]}" "${base}srt")
  fi

  if [[ -f "${base}idx" ]]; then
    args=("${args[@]}" "${base}idx")
  fi

  # create output file
  mkvmerge "${args[@]}"
done

Categories: merging, misc