Ubuntu bash script - Searching all folders to run Video Conversion - bash

I've recently gotten my hands on a Google Chromecast and realised that almost none of my current media will work "well" with it (most is mkv with ac3 audio)
I've been able to create a simple bash script to convert all files in a folder... but I have hundreds of folders so I'm looking to make this job recursive so I only need to run it once
Current script
for i in *.mkv; do
avconv -i "$i" -c:v copy -c:a aac -strict experimental "${i/.mkv/.mp4}"
done
What I want to do is add logic to this so that it can search through two sub folders, convert the found file, move the converted file to the root directory and remove the original file
ie
/Movies/convert.script
/Movies/Movie1/this_movie.mkv
/Movies/Movie2/that_movie.mkv
/Movies/Movie3/another_movie.mkv
becomes
/Movies/convert.script
/Movies/this_movie.mp4
/Movies/that_movie.mp4
/Movies/another_movie.mp4

To search through subfolders you can use find
find -name '*.mkv' -exec /path/to/convert-movie.sh {} \;
where /path/to/convert-movie.sh would be
#!/bin/bash
avconv -i "$1" -c:v copy -c:a aac -strict experimental "${1/.mkv/.mp4}"

Related

Using find / for how do I remove the original file extension for the output file name?

When using find or for to run things on multiple files, how would I make something not keep the file extension?
For example if using ffmpeg on multiple files to convert from DTS to WAV I would run one of the following:
find . -name "*.dts" -exec ffmpeg -i "{}" -c:a pcm_s24le "{}.wav" \;
or
for f in ./*.dts; do ffmpeg -i "$f" -c:a pcm_s24le "$f.wav"; done
Both of these make files that end in .dts.wav rather than just .wav
My goal is to find out what I would add/change to make the "{}.wav" or "$f.wav" not include the .dts part for the output file name. (and several other examples with various extensions)
This happens automatically when using the cli version of flac, the output file automatically removes .wav and has .flac instead, when no output file is specified.
(Ex: flac -8 *.wav would create .flac files next to the .wav files, but they aren't .wav.flac, they're just .flac)
You might want to use GNU parallel for this, e.g.:
find . -name '*.dts' | parallel 'echo ffmpeg -i {} -c:a pcm_s24le {.}.wav'
Remove echo when you want to execute the commands. You can control how many jobs run simultaneously with -j N.
Example
mkdir a b
touch [ab]/infile.dts
Check file-structure:
find a b
Output:
a
a/infile.dts
b
b/infile.dts
Now with parallel:
find a b -name '*.dts' | parallel 'echo ffmpeg -i {} -c:a pcm_s24le {.}.wav'
Output:
ffmpeg -i a/infile.dts -c:a pcm_s24le a/infile.wav
ffmpeg -i b/infile.dts -c:a pcm_s24le b/infile.wav

How do I search for files inside a directory, then burn the name of that directory into the files inside using ffmpeg?

I'm struggling to articulate this question- but I'm after a way of searching for files inside any directory that may exist in my search area (ie. not specifying the name of the directory), then using that directory name as a burn in on the files I have inside it, using ffmpeg?
So for example, say I had a folder with my script inside it. I've just created a folder called "day 01" inside the folder with the script, with some mxf files inside that. If I run my script, I want it to find the mxf files inside "day 01" then run ffmpeg and have that write "day 01" as a burn in on the picture of those mxf files.
I know how to do the burn in, I just don't know how to reference the directory "day 01".
Hope that makes sense. Thanks in advance.
method one of the many:
#!/bin/bash
find . -type f -name '*.mxf' -print0 | while read -d $'\0' INP
do
TXT="${INP%/*}"
TXT="${TXT##*/}"
echo "${INP} - ${TXT} - ${INP%.*}_txt.mp4"
ffmpeg -i "$INP" -filter_complex "
drawtext=text='${TXT}':
fontsize=h/30:
x=(w-text_w)/2:
y=(h-text_h*2):
fontcolor=white[v]
" -map [v] -map 0:a -c:v h264_nvenc -cq 23 -c:a aac -q:a 4 "${INP%.*}_txt.mp4" -hide_banner -y < /dev/null
done

FFmpeg: Encode multiple video files at once (batch)

How do I encode multiple video files in one batch with ffmpeg, using the same settings?
I found this one-line solution that converts .avi files in the current folder to .mov. Note that I want to encode .mov -> .mov :
for i in *.mov; do ffmpeg -i "$i" "${i%.mov}.mov" ; done
I wish to use the following settings for encoding:
ffmpeg -i "$i" -c:v libx265 -preset ultrafast -crf 20 -af "volume=25dB, highpass=f=200, equalizer=f=50:width_type=h:width=100:g=-15" -c:a aac -strict experimental -b:a 192k OUTPUT-ENCODED.MOV
Possible ways to prevent overwriting:
Add -ENCODED to the end of the filename before the file extension
Rename the files to something sequential, like OUTPUT01.MOV, OUTPUT02.MOV, etc
Put the encoded files in a directory subfolder but with the same file names
You can freely manipulate the output file ${i%.mov}.mov - here, the "key ingredient" is that the statement ${i%.mov} yields content of variable i with the shortest match of .mov deleted from the back. For details see this tutorial on manipulating strings in bash.

Issue with overwriting file while using ffmpeg for converting

I'm using ffpmeg to convert all my videos to mp4:
ffmpeg -i InpuFile -vcodec h264 -acodec aac -strict -2 OutputFile.mp4
The problem is, if I'm overwriting the input file, i.e the output and input files are the same:
ffmpeg -i InpuFile -vcodec h264 -acodec aac -strict -2 InpuFile.mp4 -y
or
ffmpeg -i InpuFile -y -vcodec h264 -acodec aac -strict -2 InpuFile.mp4
the new file is not good. He lasts one second and his size is extremely small.
Any ideas?
I want to use this as a script in my server so the overwriting is the most convinient way for me, I prefer that way instead of creating temporary files then replacting the temporary with original.
I had this same (frustrating) problem, you may have noticed that this happens because ffmpeg is writing over the file that it's reading, you are corrupting the source before the process finish... ffmpeg doesn't put the file in some buffer, so you can't do this way, you will have to use a temporary file.
just in case
You cannot overwrite the input file while you are encoding. You must encode to an different output file.
Afterwards, you can replace the original file with the new encoded file.
As others have mentioned, there is no way to do this without creating a temp file. You mentioned that you wanted to compress all your videos, and for it to be convenient. Here is a bash one-liner I used to compress all MP4 & MOV inside a directory:
find * -type f \( -iname \*.mp4 -o -iname \*.mov \) -execdir ffmpeg -i {} -vcodec libx265 -crf 24 temp_{} \; -execdir mv temp_{} {} \;
The -crf param controls the video bitrate. It's value ranges from 18-24, lower value is higher bitrate.
If you just wanted to compress .mp4 for example then you'd change the command to:
find * -type f -iname "*.mp4" -execdir ffmpeg -i {} -vcodec libx265 -crf 24 temp_{} \; -execdir mv temp_{} {} \;
Hope this helps OP, or anyone looking to do something similar.
Neither is too annoying tmp file use. In one line:
input="InpuFile"; ffmpeg -i "$input" -y -vcodec h264 -acodec aac -strict -2 "/tmp/$input";rm "$input"; mv "/tmp/$input" .;

How can I automatically convert all MP4 files to FLV with ffmpeg?

How can I automatically convert all MP4 files to FLV in a specific folder?
ffmpeg -i VID00002.MP4 -ar 44100 test.flv
Is there a way to queue these tasks, assuming that I don't know the file names?
If I need to run any scripts (I'm familiar with Python), how can I do that?
You can do this fairly easy within the terminal, given you have ffmpeg installed. In your terminal, enter the following:
$>cd /your/path/to/videos
$>for i in *.mp4; do ffmpeg -i $i -ar 44100 $i.flv; done
The second command simply iterates through each mp4 file and assigns the filename to '$i'. You then call ffmpeg using $i as the input and output filename. For the output, you simply add the extension, in this case $i.flv. So, if your filename is 'video.mp4', it will output as 'video.mp4.flv'.
Hope this helps.
This will convert and rename the new files using the find and ffmpeg functions and suppressing output questions:
find /mymediapath (\ -name '*.mp4' \) -exec bash -c 'ffmpeg -y -i "$0" -strict -2 "${0/mp4/flv}"' {} \;

Resources