mac terminal ffmpeg batch recursive conversion preserving directory structure - macos

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

Related

How do you convert to the same format an entire directory with subfolders using ffmpeg?

How to keep the file name and the extension the same and append _backup to the old file?
I have had tried this
find . -name "*.mp4" -exec bash -c 'for f; do ffmpeg -i "$f" -codec copy "${f%.*}.mp4"; done' -- {} +
but here the files would be overwritten.
I hope what I have requested is possible.
I suggest you append "_backup" to your input files first, then process the just renamed files with ffmpeg:
Simple for-loop to process files in current directory:
for f in *.mp4; do
mv "$f" "${f%.*}_backup.mp4"
ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"
done
#or single-line:
for f in *.mp4; do mv "$f" "${f%.*}_backup.mp4"; ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"; done
find to process files in current directory and sub directories:
find -name "*.mp4" -exec bash -c '
f="{}"
mv "$f" "${f%.*}_backup.mp4"
ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"
' \;
#or single-line:
find -name "*.mp4" -exec bash -c 'f="{}"; mv "$f" "${f%.*}_backup.mp4"; ffmpeg -i "${f%.*}_backup.mp4" -c copy "$f"' \;

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 ... {} \;

Bash loop over directory tree with ffmpeg, wrong spaces? [duplicate]

This question already has answers here:
Iterate over a list of files with spaces
(12 answers)
How to loop through file names returned by find?
(17 answers)
Closed 5 years ago.
I am trying to interate over multiple video files that are grouped in directories, and ffmpeg returns errors about paths, it seems like paths are broken, end at first space. Can you point me too what is the problem here? Files and directories have spaces.
$ for f in $(find -type f -name *.mkv); do ffmpeg -n -i "$f" -c:v copy "~/Pobrane/$f" ; done
Loop splits paths by space and takes words as entries. How to fix this?
$ for f in $(find -type f -name *.mkv); do echo "$f"; done
./homeworks/2017-04-03
00-54-57
homework3b.mkv
./homeworks/2017-04-03
00-21-36
homework1.mkv
./homeworks/2017-04-03
Replacing the Loop
Use
find -type f -name '*.mkv' -exec ffmpeg -n -i {} -c:v copy ~/Pobrane/{} \;
-exec executes the following ffmpeg command for each of the found paths, replacing {} with the current path. The ; informs find that the ffmpeg command ends there.
Quote '*.mkv' in order to pass the literal string to find, which then searches for files ending with *.mkv. If you do not quote the string and have some mkv files laying around in your working directory, the shell will expand the unquoted *.mkv resulting in find -type f -name firstFile.mkv secondFile.mkv ... before starting find.
Do not quote ~. The unquoted ~ expands to your home directory (probably /home/yourname) but the quoted '~' is a directory/file with the literal name ~ .
Creating Parent Directories
How would I add mkdir -p before the ffmpeg call?
You could wrap the mkdir and ffmpeg in one function and execute the function:
myFunction() {
mkdir -p "$(dirname "$1")"
ffmpeg -n -i "$1" -c:v copy ~/Pobrane/"$1"
}
export -f myFunction
find -type f -name '*.mkv' -exec bash -c 'myFunction "$0"' {} \;
or use a loop:
find . -type f -iname "*.txt" -print0 | while IFS= read -r -d $'\0' file; do
mkdir -p "$(dirname "$file")"
ffmpeg -n -i "$file" -c:v copy ~/Pobrane/"$file"
done

How to pipe multiple files to ffmpeg?

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

Bash script to search and rename recursively

I have a bash script that converts *.mkv files to *.avi files. Here's what it looks like:
#!/bin/bash
for f in $(ls *mkv | sed ‘s/\(.*\)\..*/\1/’)
do
ffmpeg -i $f.mkv -sameq $f.avi
done
What I need this script to do however, is it needs to search recurssively in all folders for *.mkv files and then run the ffmpeg command and save the output to the same directory.
PLEASE can someone help me? :-)
find /some/path -name '*.mkv' | while read f
do
ffmpeg -i "$f" -sameq "${f:0:-4}.avi"
done
Try like this:
find <file_path> -name '*.mkv' -exec sh -c 'mv "$0" "${0%%.mkv}.avi"' {} \;
#!/bin/bash
find . -name "*.mkv" -exec ffmpeg -i {} -sameq `basename {} .mkv`.avi \;
Thanks to #Raul this is what worked for me and is the solution to what I wanted to do which is run recursively through directories and run the ffmpeg command on mkv files:
#!/bin/bash
find <file_path> -name '*.mkv' -exec sh -c 'ffmpeg -i "$0" -sameq "${0%%.mkv}.avi"' {} \;
exit;
Instead of ls *.mkv use find . -name "*.mkv".
This assumes no funny filenames (no spaces, newlines). Another possibility is using find in conjunction with xargs. The xargs manual makes for an instructive reading which will save your scripting life one day :-)

Resources