Print results at conclusion of bash 'for loop' - bash

I'm a beginner.
I have a Bash script that embeds subtitles into mkv files if they exist in a directory.
for i in *.mkv; do
if [ -f "${i%mkv}"*"srt" ]; then
ffmpeg -i "$i" -f srt -i "${i%mkv}"*"srt" -map 0:0 -map 0:1 -map 1:0 -c:v copy -c:a copy -c:s srt $i.output.mkv
mv "${i%mkv}"*"srt" "${i%mkv}srt".old
mv $i $i.old
mv $i.output.mkv $i
else
echo $i "does not have srt file"
fi
done
It looks for all .mkv files that have an associated .srt file and does some ffmpeg magic to it. If it does not find an associated .srt file it says that the .mkv file "does not have srt file."
How can I make it so that at the conclusion of the for loop I get a print out of all the .mkv files that did have an .srt file and did successfully do all the other actions?
Thank you.

Let's use a Bash array to store completed or skipped MKVs:
SUCCESSMKVS=()
SKIPMKVS=()
for i in *.mkv; do
if [ -f "${i%mkv}"*"srt" ]; then
ffmpeg -i "$i" -f srt -i "${i%mkv}"*"srt" -map 0:0 -map 0:1 \
-map 1:0 -c:v copy -c:a copy -c:s srt "${i}.output.mkv"
mv "${i%mkv}"*"srt" "${i%mkv}srt".old
mv "$i" "${i}.old"
mv "${i}.output.mkv" "$i"
SUCCESSMKVS+=("$i")
else
echo $i "does not have srt file"
SKIPMKVS+=("$i")
fi
done
echo "The following MKVs succeeded:"
for mkv in "${SUCCESSMKVS[#]}"; do
echo -e "\t${mkv}"
done
echo
echo "The following MKVs were skipped:"
for mkv in "${SKIPMKVS[#]}"; do
echo -e "\t${mkv}"
done
echo

Related

How to apply code to a file of a given extension

I've been messing around with Apple's Automator & Quick Actions, and have run into a snag. I want to be able to engage a script that will reverse every audio file in that folder (and paste the reversed audio into a new folder). I want it to be able to work with a range of audio types, and that's my problem. I can't seem to figure out how to work with if statements in this context. I'm very new to terminal commands, so it could be that I'm doing something completely wrong. The quick action will run without throwing up any errors, but all it does is make the new folder.
for f in "$#"
do
cd "$f"
mkdir reversed
if [[ f == *.m4a ]];
then
/opt/homebrew/bin/ffmpeg -nostdin -i "$name" -af areverse -c:a alac -c:v copy "reversed/${name%.*}.m4a"
elif [[ f == *.aiff ]];
then
/opt/homebrew/bin/ffmpeg -nostdin -i "$name" -af areverse -c:a pcm_s16le -c:v copy "reversed/${name%.*}.aiff"
elif [[ f == *.mp3 ]];
then
/opt/homebrew/bin/ffmpeg -nostdin -i "$name" -af areverse -c:a mp3 -c:v copy "reversed/${name%.*}.mp3"
fi
done
Hopefully this makes sense, and thank you in advance!
First, you are matching a literal string against the patterns, not the value of the parameter. Second, you are trying to match the directory name, not names of files in the directory. Something like
process () {
/opt/homebrew/bin/ffmpeg -nostdin -i "$1" -af areverse -c:a "$2" -c:v copy "reversed/${1%.*}".$3
}
for d in "$#"; do
cd "$d"
mkdir reversed
for f in *; do
case $f in
*.m4a) process "$f" alac m4a ;;
*.aiff) process "$f" pcm_s16le aiff ;;
*.mp3) process "$f" mp3 mp3 ;;
esac
done
done

ffmpeg / batch addition of watermarks for videos [duplicate]

This question already has answers here:
How do you convert an entire directory with ffmpeg?
(34 answers)
Closed 1 year ago.
I used with script to add dynamic watermark to one video, how to adapt it to convert multiple videos?
ffmpeg -i test.mp4 -i logo.png -filter_complex "[1]colorchannelmixer=aa=0.6,scale=iw*0.7:-1[a];[0][a]overlay=x='if(lt(mod(t\,16)\,8)\,W-w-W*5/100\,W*5/100)':y='if(lt(mod(t+4\,16)\,8)\,H-h-H*2.5/100\,H*2.5/100)'" -c:v libx264 -an out.mp4
You just need to iterate the video files:
source_folder=$1
target_folder=$2
mkdir -p $target_folder
echo "procesing..."
for file in $source_folder/*.mp4 $source_folder/**/*.mp4 ; do
if [[ -f $file ]]; then
filename=$(basename -- "$file")
echo "source video:"$file "new :"$target_folder/$filename
ffmpeg -i "$file" -i logo.png -filter_complex "[1]colorchannelmixer=aa=0.6,scale=iw*0.7:-1[a];[0][a]overlay=x='if(lt(mod(t\,16)\,8)\,W-w-W*5/100\,W*5/100)':y='if(lt(mod(t+4\,16)\,8)\,H-h-H*2.5/100\,H*2.5/100)'" -c:v libx264 -an "$target_folder/$filename"
fi
done;
echo ""
echo "result: $target_folder"
find $target_folder | sort
Example:
bash script.sh /input /foo/bar/output

Using ffmpeg to convert mp3 to mp4 - how to draw filename on generated video?

Using trial and error I am using the following script on an OSX, to bulk convert a whole folder full of mp3 files, to mp4, by looping a specific video file:
for i in *.mp3; do /usr/local/bin/ffmpeg -stream_loop -1 -i /path_to_filename.mp4 -c copy -v 0 -f nut - | /usr/local/bin/ffmpeg -thread_queue_size 10K -i - -i "$i" -c copy -map 0:v -map 1:a -shortest "$(basename "$i" )".mp4 ; done; for f in *.mp3.mp4; do mv -v "$f" "${f/.mp3.mp4/.mp4}"; done
How can I also print/add/burn the mp3 filename, without the extension (.mp3), as an additional video layer at the bottom of the generated video screen, and with the added difficulty of word wrapping the text if is too long?
This command makes a temporary SRT file for the subtitles filter which will automatically deal with the placement and word wrapping:
for i in *.mp3; do echo "1" > subs.srt; echo "00:00:00,000 --> 10:00:00,000" >> subs.srt; echo "${i%.*}" >> subs.srt; ffmpeg -stream_loop -1 -i video.mp4 -i "$i" -filter_complex "[0:v]subtitles=subs.srt:force_style=Alignment=3,format=yuv420p[v]" -map "[v]" -map 1:a -c:v libx264 -c:a copy -shortest -movflags +faststart "${i%.*}.mp4"; done
Running the command in the directory containing the file allows avoidance of basename and creates a simpler command.
MP3 is not universally supported in MP4. Consider changing -c:a copy to -c:a aac if your player does not support it. But I suspect you're targeting YouTube which will be fine with MP3 in MP4.
The three instances of echo are inefficient but effective in avoiding any newline issues. Using one instance of printf would be more optimal, but I don't have access to macOS to test its printf implementation.

/usr/local/bin/ffmpeg: cannot execute binary file

Want to convert my m4a files into mp3 files using a script. It would save some time... I have over 100 GB of music files.
OS: OSX10.14 / Terminal vs Bash script
I can run ffmpeg -v 5 -y -i musicFile.m4a -acodec libmp3lame -ac 2 -b:a 320k musicFile.mp3 from the terminal. It converts the file and I can see and play the file from itunes.
When I run the same from a bash script it fails to convert.
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
My ipod nano just died and I got a new mp3 player. Now I need to convert my itunes files from AAC format to MP3.
ffmpeg is an established video and music file converter.
When I run it from the bash script I tried a few things.
I added ./ in front of the file, that failed because it was installed under /usr/local/bin and not under the same directory.
I also tried sh ffmpeg... and that gave me the cannot execute a binary file.
#!/usr/bin/env bash
# convert m4a file to mp3
set -e
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
printf "%*s%s\n" $((indent+2)) '' "$ENTRY_FILE"
printf "%*s\tNew File :\t%s\n" $((indent+2)) '' "$MP3NAME"
if [ $EXT == "m4a" ]
then
printf "%*s\tConverting: \t%s\n" $((index+2)) '' "$ENTRY_FILE"
ffmpeg -v 5 -y -i $ENTRY_FILE -acodec libmp3lame -ac 2 -b:a 320k $MP3NAME
fi
}
walk() {
local indent="${2:-0}"
printf "\n%*s%s\n\n" "$indent" '' "$1"
# If the entry is a file convert it
for entry in "$1"/*; do [[ -f "$entry" ]] && file_convert; done
# If the entry is a directory recurse
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+2)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || pushd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
popd
echo
I expect >./aacToMp3.sh ./music to traverses the music directory and convert each m4a file to .mp3.
It is walking the file system and printing out correct files, with the spaces escaped. When it hits the ffmpeg line it halts. I put the set -e at the top of the file to force it to fail if the command fails. Without the set -e it happily walks all the music files and prints them to the stdout.
If you have lots of files to process and a decent multi-core CPU and fast disk, I would recommend GNU Parallel which you can install with homebrew:
brew install parallel
Then make a copy of a few files in a test directory and try:
parallel --dry-run ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3 ::: *.m4a
If that looks good, replace --dry-run with --progress.
If that looks good, you can (make a backup first) and do the whole lot:
find path/to/music -name "*.m4a" -print0 | parallel -0 --progress ffmpeg -v 5 -y -i {} -acodec libmp3lame -ac 2 -b:a 320k {.}.mp3
Thanks for all the input.
After a while I ended up writing all the ffmpeg lines to a script file. Glad I did. I was able to quickly scan the file and see some errors and fix them.
This is what I came up with.
Basically I am writing straight text to stdout and directing it to a file. Which I converted to a shell script to convert each file. One at a time. Ran the generated script overnight.
file_convert() {
ENTRY_FILE=$(printf %q "${entry}")
FILE_NAME=$(printf %q "$(basename "${entry}")")
DIR=$(printf %q "$(dirname "${entry}")")
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
MP3NAME="${DIR}/${NAME}.mp3"
if [ $EXT == "m4a" ]
then
printf 'echo "Converting %s ..."\n' "$FILE_NAME"
printf 'ffmpeg -v 5 -y -i %s -acodec libmp3lame -ac 2 -b:a 320k %s\n\n' "$ENTRY_FILE" "$MP3NAME"
fi
}
The output looked like.
#!/usr/bin/env bash
echo "Converting 03\ It\'s\ Not\ My\ Time.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/3\ Doors\ Down/3\ Doors\ Down\ \(Bonus\ Track\ Version\)/03\ It\'s\ Not\ My\ Time.mp3
echo "Converting 01\ Down.m4a ..."
ffmpeg -v 5 -y -i /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.m4a -acodec libmp3lame -ac 2 -b:a 320k /Users/arthuranderson/Documents/work/projects/mp3Convert/music/311/Greatest\ Hits\ \'93-\'03/01\ Down.mp3
Mark I will try parallel when I purchase some more music next time.
Thanks everyone!

How can I tell an if statement to skip a file but continue onto the next file if a certain condition is met?

for f in ~/Desktop/Uploads/*.flv; do
if /usr/local/bin/ffprobe ${f} 2>&1 | egrep 'Stream #0:0.+flv1'; then
<what do I put here for the batch file to skip the file and continue?>
else
if /usr/local/bin/ffprobe ${f} 2>&1 | egrep 'Stream #0:1.+speex'; then
/usr/local/bin/ffmpeg -i ${f} -vn -acodec libfaac -ab 128k -ar 48000 -async 1 ${f/%.flv/.m4a}
SPEEX_ADD="-i ${f/%.flv/.m4a}"
SPEEX_MAP="-map 1:0"
SPEEX_TRASH="rmtrash ${f/%.flv/.m4a}"
~/Desktop/Uploads/ffmpeg/ffmpeg -i ${f} ${SPEEX_ADD} -vcodec copy -acodec copy -map 0:0 ${SPEEX_MAP} ${f/%.flv/.mp4} && rmtrash ${f} && ${SPEEX_TRASH}
else
~/Desktop/Uploads/ffmpeg/ffmpeg -i ${f} -vcodec copy -an ${f/%.flv/.mp4} && rmtrash ${f}
fi
done
I'm using a batch file to convert video files but I need help. What can I put in that third line that will make the batch file skip over files with flv1 video but continue onto the next file? Thanks for your help.
continue. Check the bash man page for details.
You can use the continue keyword. Have a look at the Loop Control page in the Advanced Bash-Scripting Guide

Resources