Script to compress selected video files using FFmpeg

Hello,

Looks like everyone here has their scripts to automate the routine tasks.

I have never wrote any scipt but having s set of them definitely seems as a better idea compare to running exiftool / ffmpeg / whatever commands in terminal.

Here is one of the commands I use pretty often:

ffmpeg -i in.mp4 -c:v libx264 -crf 23 -c:a libfdk_aac -b:a 192k -map_metadata 0 out.mp4

As you can see in.mp4 is in the beginning of the command and out.mp4 is at the end of the command which makes drag’n’drop files to terminal a pain in the neck.

What I would like to do is to select files in Dolphin - Right Click and select a script I want from the menu.
For this particular command it would use selected files as inputs and save the output files in the same directory while adding codec name to the end of the file name. For example:
/home/user/video_file_1.mp4 would be converted and saved as /home/user/video_file_1_x264.mp4

I was wondering if anyone has a similar script already written that could be shared or if there is any good documentation on the web that would help me start writing my scripts.

Since there are many languages out there (bash, python, etc.) and I am not familiar with any of them I would start with the one that is:

  1. Easy to learn (faster to start writing)
  2. Linux Desktop Environment undependable (I am on KDE at the moment but I don’t want to lock myself into it).

Any thoughts would be greatly appreciated.

If you want to right click in dolphin, you’ll likely end up with a solution that is desktop dependant, which is at odds with what you asked for near the end of the post.

For a first scripting language, I always say bash, it’ll get you most of what you need in the beginning, despite being a little quirky. It’ll also just let you run commands that you’d run in the normal terminal.

For your script, I’d just run it from the terminal, not from dolphin, and I’d replace in.mp4 and out.mp4 with $1 and $2 and put it all in a shell script. Then you can call scriptName.sh in.mp4 out.mp4 and it’ll use the command in the script, subbing $1 and $2 from the script with what you’ve specified.

You can run it from Dolphin - create a .desktop file which calls your script, then look into the Free Desktop specification to learn about variables for these .desktop files:
https://specifications.freedesktop.org/desktop-entry-spec/latest/index.html

Do you think I should create a .desktop file and use ffmpeg as executable with arguments?

ffmpeg -i %F -c:v libx264 -crf 23 -c:a libfdk_aac -b:a 192k -map_metadata 0 %u/%F_x264

Code - Description
%f - A single file name (including the path), even if multiple files are selected. The system reading the desktop entry should recognize that the program in question cannot handle multiple file arguments, and it should should probably spawn and execute multiple copies of a program for each selected file if the program is not able to handle additional file arguments. If files are not on the local file system (i.e. are on HTTP or FTP locations), the files will be copied to the local file system and %f will be expanded to point at the temporary file. Used for programs that do not understand the URL syntax.
%F - A list of files. Use for apps that can open several local files at once. Each file is passed as a separate argument to the executable program.
%u - A single URL. Local files may either be passed as file: URLs or as file path.

Sounds like you’re on the right path!

No, write a Bash script and call the script from the .deskop file, passing the necessary arguments.

Yeah, looks like it is a right path for me. I will be able to call the bash script from any file explorer / terminal later on.

My script is now working but it tries to convert all files in the folder to mp4 e.g. jpegs and creates mp4-named copies of the jpegs. I am using
For ‘ls’ in filename

And then
ffmpeg -i $filename …

I am wondering if there is a better way to search for files by extension and export the output to an argument. I could not make “find” command working for me. Been googling for last 2 hours.
Any ideas?

Show the code.

1 Like

The solution was:
for filelist in *.mp4;

I missed ; and spent hours searching forums…:grimacing:

1 Like

@Andrius I had time to take a look at your issue today.

Here you will find a script which does what you want - it transcodes any number of input files one by one - following Bash best practices:

I hope it helps you learn a thing or two.
There’s a lot of bad advice floating about regarding Bash. Here are the two best Bash resources you can trust:
http://mywiki.wooledge.org/FullBashGuide
http://wiki.bash-hackers.org/syntax/pe
http://wiki.bash-hackers.org/commands/classictest

2 Likes

Hello Morgan,
Thank you very much for taking the time to look into this.
I wish I saw your script before writing mine. :slight_smile:

The script works just fine but what I what I found out is that actually some metadata gets lost/changed during the ffmpeg conversion even though the -map_metadata 0 option is activated.
Bento4 SDK Suit can copy the atoms containing the metadata from the original file to the compressed one.
So I am trying to add the following actions to the script:
First of all, download bento4 SDK and add the arguments to the script: (I guess you can skip this step if you compiled it from sources.)
mp4extract="/path/to/static/build/mp4extract"
mp4edit="/path/to/static/build/mp4edit"
Then
# Do the conversion.
count="1"
for f in "$@"; do
Step 1 - Extraction of the udta atom from each video file (filename.mp4) to a corresponding txt file (filename.txt):
mp4extract moov/udta "${f}" ${f%.*}.txt
Step 2 - Compression of each original video file (filename.mp4) to filename_suffix.mp4 using ffmpeg:
ffmpeg -i "${f}" -y -f mp4 -c:a "${ca}" -b:a "${ba}" -c:v "${cv}" -crf "${crf}" -preset "${preset}" -map_metadata 0 "${f%.*}_${cv}_${crf}_${preset}.mp4" || exit 1
Step 3 - Import the metadata from the txt files (filename.txt) to the compressed mp4 videos (filename_suffix.mp4).
Here is the code I wrote for this:
for FILE in *.txt;
do
BASE=${FILE%.txt}
if [ -e "$BASE.txt" ]; then
echo -e "\033[1;34m Importing metadata from "$BASE.txt" to "$BASE.mp4" / "$BASE_gps.mp4"\033[0m"
mp4edit --insert moov:"$BASE.txt" "$BASE.mp4" "$BASE_gps.mp4"
fi
done
but I don’t know if the code actually works. I simply don’t get to the step 3 because the step 1 does not work.
The command works just fine when I run it in the terminal and drag a file there, e.g.
/path/to/Bento4-SDK-1-5-0-614.x86_64-unknown-linux/bin/mp4extract moov/udta '/path/to/filename.mp4' '/path/to/filename.txt'
but if mp4extract being called via a script I get this error:
Open: open(moov/udta) failed (src/mp4file.cpp,398)
I am wondering why there is a difference between running mp4extract in the terminal and calling it via a bash script. Any ideas?
https://www.bento4.com/

I suggest you build whatever changes you need on top of my file because the code above continues the mistakes I found in your original.

As for copying metadata, I’d first try the far more common and readily avialable ExifTool:
exiftool -TagsFromFile in.mp4 out.mp4
http://www.sno.phy.queensu.ca/~phil/exiftool/exiftool_pod.html#COPYING-EXAMPLES

@Morgan_Hardwood I just finished working on version 1.0 of the script and wondering what is the right way to publish it on GitHub. I created a repository but not sure how I can upload a file there. Also I can create a gist but I don’t know what gist is. Gists are a pain in the neck to find from mobile device as well. Can you advise ?

Do you want to publish to the pixls repo or your own?

It does not really matter for me.I bet my scripts will need to be polished a lot prior to be published at pixls.us

If you like to add it to the pixls repo, you’d for the repo, add your changes, then make a pull request on github.

It makes a clone of the whole repo when I do this. Is this fine ?

Yes, you’ll add your change to your fork of the repo, push back to github, then it should prompt you to make a pull request.