How to parallelize Nautilus scripts

I’m using a number of Nautilus scripts for media production, e.g. to easily convert files to JPEG or MP3 format with pre-defined settings. Nautilus doesn’t parallelize script execution, but it’s not so hard to create a script which does.

Here’s a script for converting pictures to JPEG using ImageMagick:

#!/bin/bash

MY_NAME=$(basename "$0")
NUM_PROCS=$(($(nproc) * 15 / 10))

IFS=' ' read -r -a COMMAND_TOKENS <<<"$0"
QUALITY=${COMMAND_TOKENS[${#COMMAND_TOKENS[@]}-1]}

for FILE in "$@"; do printf "%s\0" "$FILE"; done | xargs -0 -P ${NUM_PROCS} -n 1 -I '{}' convert "{}" -quality ${QUALITY} "{}.${QUALITY}.jpg"

notify-send "${MY_NAME} is finished, $# files converted."

The trick is that this does not just parallelize using xargs (I heavily dislike GNU Parallel because of its “pay 10,000 $ or cite me” blackmail scheme), the last token of the script file name is also a parameter to the script itself. So the same script can appear multiple times in the context menu and use a different JPEG quality setting each time!

xargs is instructed to schedule 50% more processes than the number of CPUs reported by the system, which might be too few or too many, depending on your machine and use case. When the script is finished, an OSD notification will pop up.

This script appears three times in my home directory:

~/.local/share/nautilus/scripts/Convert to JPEG quality 75
~/.local/share/nautilus/scripts/Convert to JPEG quality 85
~/.local/share/nautilus/scripts/Convert to JPEG quality 96

The last two are just symlinks to the first one. Now I can quickly convert a lot of pictures to JPEG with quality levels 75, 85 or 96, from the Nautilus context menu, in parallel.

Here’s my script for converting audio files to MP3 using ffmpeg:

#!/bin/bash

MY_NAME=$(basename "$0")
NUM_PROCS=$(($(nproc) * 15 / 10))

IFS=' ' read -r -a COMMAND_TOKENS <<<"$0"
echo $COMMAND_TOKENS[3]
BITRATE=${COMMAND_TOKENS[${#COMMAND_TOKENS[@]}-1]}

for FILE in "$@"; do printf "%s\0" "$FILE"; done | xargs -0 -P ${NUM_PROCS} -n 1 -I '{}' ffmpeg -y -i {} -b:a ${BITRATE} -map_metadata 0 -id3v2_version 3 {}.mp3

notify-send "${MY_NAME} is finished, $# files converted."

I am using these path names to convert to 192, 256 or 320 kilobit/s (note the letter ‘k’ at the end, which is important):

~/.local/share/nautilus/scripts/Convert to MP3 192k
~/.local/share/nautilus/scripts/Convert to MP3 256k
~/.local/share/nautilus/scripts/Convert to MP3 320k