How to pipe multiple files to ffmpeg? - bash

I am trying to make a bash script that searches all subfolders on given path for .mov files and converts them with ffmpeg and outputs them in an destination folder, keeping the clip name.
I'm very new to scripting and I'm having a hard time finding out how to solve this.
So far I've tried using ls and find to output the filepaths, but have no idea how to pipe this to ffmpeg in the right way.
Any clues?
Edit:
got some sucess with this:
#!/bin/bash
echo "drop source folder: "
read source
echo "drop destination folder: "
read des
find "$source" -name '*.mov' -exec sh -c 'ffmpeg -i "$0" -vcodec prores -profile:v 0 -an "$des/${0%%.mov}.mov"' {} \;
exit;
but, the it seems to output to the source folder asking for a overwrite. How can i setup the parameters correctly so it outputs to the "destination folder" and keeps the filenames?

You could start with this:
#!/bin/bash
shopt -s extglob || {
echo "Unable to enable exglob."
exit 1
}
TARGETEXT='.avi'
TARGETPREFIX='/path/to/somewhere/' ## Make sure it ends with /.
while IFS= read -r FILE; do
BASE=${FILE##*/}
NOEXT=${BASE%.*}
TARGETFILEPATH=${TARGETPREFIX}${NOEXT}${TARGETEXT}
echo ffmpeg -i "$FILE" "$TARGETFILEPATH" ## Remove echo if it's already correct.
done < <(exec find -type f -name '*.mov') ## You could also use -iname '*.sh' instead.
Of course you could use a custom directory to search for the files:
find /path/to/directory -type f -name '*.mov'

something like this should do the job:
for f in *.mov; do ffmpeg -i "$f" -vcodec copy -acodec copy "/desination/${f%.mov}.mp4"; done

Related

mac terminal ffmpeg batch recursive conversion preserving directory structure

i'm using ffmpeg on mac to batch convert .flv to .mp4 files. i'm trying to find all files in subdirectories of the current directory and save new files in the same directory.
for instance starting with:
subdirectory1/video1.flv
subdirectory1/video2.flv
subdirectory2/video1.flv
and ending with
subdirectory1/video1.mp4
subdirectory1/video2.mp4
subdirectory2/video1.mp4
i've gotten this far but can't figure out how to save with preserved recursive directories
for i in `find -name . "*.flv"`; do ffmpeg -i "$i" "${i%.*}.mp4"; done
maybe there was a better way but this ultimately worked for my purposes. i had to rename the files and directories to remove spaces and rework the find command. the xargs inclusion tried to account for spaces but it didnt work
so i removed spaces from directories with this:
for f in *; do mv "$f" `echo $f | tr ' ' '_'`; done
and removed spaces from filenames with this
find . -type f -name "* *.flv" -exec bash -c 'mv "$0" "${0// /_}"' {} \;
then this command recursively reencoded my flv files and saved in their directory
for i in `find . -name "*.flv" -print0| xargs -0`; do ffmpeg -i "$i" -c:v libx264 -f mp4 "${i%.*}.mp4"; done

Use parameter expansions in a command run from "find | xargs" to prevent output overwriting

I have this bash script that is looking for mp4 files in subfolders with certain names and saves frames of those videos as jpeg.
#!/bin/bash
find ../folder -type f -iname '*C00*.mp4' | xargs -I %% ffmpeg -i %% -vf fps=1 -q:v 3 "../frames/_${i%.*}_frame%d.jpg"
The problem is that everytime the script finishes one video the .jepg output files of the next videos are overwriting the existing ones.
How can I prevent that?
Here's a quick stab which creates a directory with the same name as the input file with any .mp4 extension trimmed off.
#!/bin/bash
find ../folder -type f -iname '*C00*.mp4' -print0 |
xargs -r0 sh -c 'for f; do
d="../frames/${f%.[Mm][Pp]4}"
mkdir "$d" || { echo "$d already exists" >&2; exit 123; }
ffmpeg -i "$f" -vf fps=1 -q:v 3 "$d/frame%d.jpg"
done' _

Running a bash script recursively and performing operations on all files within the subdirectories

I'm trying to convert flac files into wav files using ffmpeg. The flac files are located in various subdirectories.
/speech_files
/speech_files/201/speech1.flac
/speech_files/201/speech2.flac
/speech_files/44/speech45.flac
/speech_files/44/speech109.flac
/speech_files/66/speech200.flac
/speech_files/66/speech33.flac
What I want after the script runs is the following
/speech_files
/speech_files/201/speech1.wav
/speech_files/201/speech2.wav
/speech_files/44/speech45.wav
/speech_files/44/speech109.wav
/speech_files/66/speech200.wav
/speech_files/66/speech33.wav
I can get my script to work within one directory but I'm having a hard time getting it to run from the top level directory (speech_files) and work it's way through all the subdirectories. Below is the script I'm using.
#!/bin/bash
for f in "./"/*
do
filename=$(basename $f)
if [[ ($filename == *.flac) ]]; then
new_file=${filename%?????}
file_ext="_mono_16000.wav"
wav_file_ext=".wav"
ffmpeg -i $filename $new_shits$wav_file_ext
ffmpeg -i $new_file$wav_file_ext -ac 1 -ar 16000 $new_file$file_ext
rm -f $filename
rm -f $new_file$wav_file_ext
fi
done
Use find from the top level directory and filter by using *.flac.
for f in $(find . -name "*.flac"); do
echo "$f" # f points to each file
# do your logic here
done
Using bash only :
#!/bin/bash
DIR="/.../speech_files"
process() {
filename=$(basename "$1")
# ...
}
for f in n "${DIR}"/*/*.flac; do
process "$f"
done
Using find which is recursive and more efficient to do that kind of task to me :
find "${DIR}" -type f -a -iname "*.flac" -exec ... {} \;

for loop work at multiple directory depth

I have the following for loop in bash:
for file in "$1"/*PM.mov ; do
ffmpeg -i "$file" -an -f framemd5 "${file}.framemd5.txt"
done
I want to adjust it so that it will run on any *PM.mov file in a given directory regardless of the directory depth of that file. Right now the loop only runs on the top level of the directory. How do I change that?
Two possibilities:
Use globstar (and nullglob while we're at it):
shopt -s globstar nullglob
for file in "$1"/**/*PM.mov ; do
ffmpeg -i "$file" -an -f framemd5 "${file}.framemd5.txt"
done
Use find properly (but the previous one is better, as it's only a minor change to your code):
find "$1" -type f -name "*PM.mov" -exec sh -c 'file=$1; ffmpeg -i "$file" -an -f framemd5 "$file.framemd5.txt"' sh {} \;
use the find cmd
for file in $(find $1 -name "*PM.mov") ; do
ffmpeg -i "$file" -an -f framemd5 "${file}.framemd5.txt"
done

Need help on ffmpeg batch script

Trying to make a ffmpeg batch scripts that makes proress 422 proxy files from raw .mov files thats located in many subfolders. So far i got this:
#!/bin/bash
echo "drop source folder: "
read source
echo "drop destination folder: "
read des
find "$source" -name '*.mov' -exec sh -c 'ffmpeg -i "$0" -vcodec prores -profile:v 0 -an "$des/${0%%.mov}.mov"' {} \;
exit;
But it doesn't output to the destination folder, only the source folder and ask for an overwrite - not what I want.
I guess it's a simple parameter error that maybe some of you can spot out?
All help is kindly appreciated!
This would be my suggestion. Looks like this was follow-up from your previous thread.
#!/bin/bash
[ -n "$BASH_VERSION" ] || {
echo "You need Bash to run this script."
exit
}
shopt -s extglob || {
echo "Unable to enable exglob."
exit 1
}
until
read -erp "Drop source folder: " SOURCE
[[ -d $SOURCE ]]
do
echo "Drop source folder does not exist: $SOURCE"
echo "You can abort this script by pressing CTRL-C."
done
until
read -erp "Drop destination folder: " DES
[[ -d $DES ]]
do
echo "Drop destination folder does not exist: $DES"
echo "You can abort this script by pressing CTRL-C."
done
while IFS= read -r FILE; do
BASE=${FILE##*/}
NOEXT=${BASE%.???}
echo ffmpeg -i "$FILE" -vcodec prores -profile:v 0 -an "$DES/${NOEXT}.mov" ## Remove echo if it's already correct.
done < <(exec find "$SOURCE" -type f -name '*.mov') ## You could also use -iname '*.sh' instead to also match files like .MOV.
Notes: Using -e option for read is helpful to easily generate paths by pressing tab.
I guess you need to export the des variable.
In this kind of cases I always suggest to insert a "echo" just before the target command ("ffmpeg")
find "$source" -name '*.mov' -exec sh -c 'echo ffmpeg -i "$0" -vcodec prores -profile:v 0 -an "$des/${0%%.mov}.mov"' {} \;
This way you will see what you are trying to run

Resources