Execute "ffmpeg" command in a loop [duplicate] - bash

This question already has answers here:
While loop stops reading after the first line in Bash
(5 answers)
Closed 2 years ago.
I have three .wav files in my folder and I want to convert them into .mp3 with ffmpeg.
I wrote this bash script, but when I execute it, only the first one is converted to mp3.
What should I do to make script keep going through my files?
This is the script:
#!/bin/bash
find . -name '*.wav' | while read f; do
ffmpeg -i "$f" -ab 320k -ac 2 "${f%.*}.mp3"
done

Use the -nostdin flag to disable interactive mode,
ffmpeg -nostdin -i "$f" -ab 320k -ac 2 "${f%.*}.mp3"
or have ffmpeg read its input from the controlling terminal instead of stdin.
ffmpeg -i "$f" -ab 320k -ac 2 "${f%.*}.mp3" </dev/tty
See the -stdin/-nostdin flags in the ffmpeg documentation

If you do need find (for looking in subdirectories or performing more advanced filtering), try this:
find ./ -name "*.wav" -exec sh -c 'ffmpeg -i "$1" -ab 320k -ac 2 "$(basename "$1" wav).mp3"' _ {} \;
Piping the output of find to the while loop has two drawbacks:
It fails in the (probably rare) situation where a matched filename contains a newline character.
ffmpeg, for some reason unknown to me, will read from standard input, which interferes with the read command. This is easy to fix, by simply redirecting standard input from /dev/null, i.e. find ... | while read f; do ffmpeg ... < /dev/null; done.
In any case, don't store commands in variable names and evaluate them using eval. It's dangerous and a bad habit to get into. Use a shell function if you really need to factor out the actual command line.

No reason for find, just use bash wildcard globbing
#!/bin/bash
for name in *.wav; do
ffmpeg -i "$name" -ab 320k -ac 2 "${name%.*}.mp3"
done

Related

/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!

Bash while loop stops for no reason? [duplicate]

This question already has answers here:
While loop stops reading after the first line in Bash
(5 answers)
Closed 3 years ago.
I run the following bash script to rotate my mobile phone vids
while read filename ; do
nf=$(echo $filename |rev | cut -f1 -d '/'|cut -f2- -d '.' |rev)
echo $nf
rm -f ffmpeg2pass-0.log
rm -f rotate/tmp.avi
ffmpeg -i $filename -c:v libxvid -pass 1 -c:a libmp3lame -qscale:a 4 -vf "transpose=2,transpose=2" "rotate/tmp.avi"
ffmpeg -i $filename -c:v libxvid -pass 2 -c:a libmp3lame -qscale:a 4 -vf "transpose=2,transpose=2" "rotate/$nf.avi"
done <rotatelist_2
I know there are better ways to do this; I budged this together but I'm figuring out how to do the videos right so the rest don't need to look nice ;-))
However after the first run, the loop unexpectedly ends with no error message. I run similar loops for other things which work pretty well.
The echo is not called again so I guess there's something wrong with the loop itself. The linebreak in the list is a 0x0A, so it should be ok.
Use
</dev/null ffmpeg ...
</dev/null ffmpeg ...
or
ffmpeg ... </dev/null
ffmpeg ... </dev/null
to prevent ffmpeg reading from rotatelist_2 via stdin.

Run a function from while read line

I'm trying to run a function from while read line, the function contains ffmpeg commands to marge two files. but for some reason it's running the first $line and than breaks from loop.
"$filesList" contains three lines. I'm not sure what's wrong, but i can confirm with echo "$OFILE" that opener function runs three times if I comment out the ffmpeg commands, and only once with ffmpeg commands.
opener(){
OFILE="$1"
echo "$OFILE"
ffmpeg -i $opener_path -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i $OFILE -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc merge_$OFILE
mv merge_$OFILE $OFILE
rm intermediate1.ts intermediate2.ts
}
while read line; do
if [ -e "$line" ]; then
opener "$line"
fi
done <<< "$filesList"
It appears one of the ffmpeg commands is reading from standard input, which consumes the rest of the contents of $filesList before the next call to read. I'm not familiar with ffmpeg, but two possibilities:
Does -i require an argument? Your posted code doesn't set the value of opener_path, so its unquoted expansion would produce an empty string that is discarded by the shell.
How is concat:intermediate1.ts|intermediate2.ts interpreted by ffmpeg? Given the call to rm, it seems to produce a pair of files from an unknown source.

Recursing through a directory and converting music

I'm writing a quick script to go through huge amounts of music to convert all the m4as to mp3s. Most of them are already mp3s, but I'd like all of them to be mp3s. Here's what I have so far:
for f in *.m4a; do ffmpeg -i "$f" -acodec libmp3lame -ab 320 "${f%.m4a}.mp3"; done
Does it on one level, how do I integrate this ffmpeg into a find command to do it recursively?
Thanks for the help!
If you want to also loop in subfolders, you can use the globstar shell optional behavior, see the Pattern Matching section of the reference manual and the Shopt Builtin section of the reference manual as so:
shopt -s globstar
for f in **/*.m4a; do ffmpeg -i "$f" -acodec libmp3lame -ab 320 "${f%.m4a}.mp3"; done
Using find it's a bit trickier since you're using a Shell Parameter Expansion. Here's a possibility that will be 100% safe regarding files with spaces or other funny symbols in their name:
find . -name '*.m4a' -type f -exec bash -c 'ffmpeg -i "$0" -acodec libmp3lame -ab 320 "${0%.m4a}.mp3"' {} \;
This second possibility might be faster if you have a huge number of files, since bash globbing is known to be quite slow for huge number of files.
In the -exec statement of find, I'm using bash -c '...'. In this case, every parameter given after the string to be executed will be set as the positional parameters, indexed from 0, hence the $0 that appears in the code
ffmpeg -i "$0" -acodec libmp3lame -ab 320 "${0%.m4a}.mp3"
Hope this helps!

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