Need help on ffmpeg batch script - bash

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

Related

Skip files in loop which already have FINAL in their name

I want to change the CODEC H264 to H265, the archives which have H264, they have the following format:
archive.mp4
and once is changed the archive has this format:
archiveFINAL.mp4
This is my code:
#!/bin/bash
find *.mp4 | while read -r file; do
if [ "${file%.*}" != "${file%.*}FINAL" ]; then
ffmpeg -i "${file%.*}".mp4 -c:v libx265 -vtag hvc1 "${file%.*}FINAL".mp4
else
echo "There is not archives to convert"
fi;
done
I made test with archiveFINAL.mp4 and the script works when in theory if the name is archiveFINAL.mp4 it doesn't have to work.
The problem is the code works without discriminate between archive.mp4 and archiveFINAL.mp4. And I don't want to do it if the name of the archive finish in FINAL.mp
Your conditional probably doesn't do what you want, and looks much too complex anyway.
find *.mp4 | while read -r file; do
if [ "${file%FINAL.mp4}" = "$file" ]; then
ffmpeg -i "$file" -c:v libx265 -vtag hvc1 "${file%.mp4}FINAL".mp4
else
echo "$file is already converted" >&2
fi
done
I would probably use a case statement instead, but this attempts to change as little as possible. The case syntax might look scary if you are not familiar with it;
case $file in
*FINAL.mp4) echo "$file is already converted" >&2;;
*) ffmpeg ...;;
esac
By the way, notice that this will recurse into subdirectories; if that's not desired, you want simply for file in *.mp4; do...
To check if the file name ends with "FINAL.mp4", you can just do it like this:
echo "$file_name" | grep -Eq '.+FINAL\.mp4$'
if [ $? -eq 0 ];then
// do what you want here
fi
Use find. It's the tool to... find stuff.
find . -maxdepth 1 -type f -name '*.mp4' '!' -name '*FINAL.mp4' |
while IFS= read -r line; do
ffmpeg -i "${file%.*}".mp4 -c:v libx265 -vtag hvc1 "${file%.*}FINAL".mp4
done
The
find *.mp4
is equal to
find somefile.mp4 otherfile.mp4 otherfile.mp4 ....
It's not using find as intended at all - *.mp4 is expanded by the shell. Would be better to use shopt -s nullglob with for i in *.mp4 instead.
To find all files named *.mp4, do find . -name '*.mp4'. Note quotation. The -maxdepth 1 limits found files to only current directory.

Code must find all MP4 files within subdirectory to compress using ffmpeg

I have a drive with a lot of MP4 files which are tough to go through folder by folder and compress.
I'm trying to make a script that runs in terminal that will open a designated folder, find all .mp4 files in the subfolder, and compress the files using specs I designate with ffmpeg. Obviously, the output files should be much lower in size if done right. I'm drafting a code which I have an idea about below but I'm not too good with BASH and/or PERL.
for f in $(find ../ -iname '*.avi'); do
n=$(echo $f|sed -e 's/.avi/_cbr.mp4/i');
echo "ffmpeg [options] -i $f $n";
done
output:
ffmpeg [options] -i ../1hourjob/videncode/sound10s.avi ../1hourjob/videncode/sound10s_cbr.mp4
ffmpeg [options] -i ../1hourjob/videncode/t003.avi ../1hourjob/videncode/t003_cbr.mp4
ffmpeg [options] -i ../ffmpeg/Masha.avi ../ffmpeg/Masha_cbr.mp4
ffmpeg [options] -i ../ffmpeg/window.avi ../ffmpeg/window_cbr.mp4
I'm wondering if I can even make some sort of GUI for this too. I feel a bit lost.
You can recursively traverse the directories in bash like this:
avi_to_mp4() {
cd "$1"
for f in *; do
if [[ -d "$f" ]]; then
avi_to_mp4 "$f"
elif [[ "$f" == *.avi ]]; then
newf="${f:0: -4}.mp4"
echo "$f" to "$newf" # run your command here
fi
done
cd ..
}
avi_to_mp4 "$1"

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' _

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

Batch converting videos in folders and sub folders

I have a huge collection of videos that all need to be converted into mp4. The folder structure is like this
Events
Chicago
Boston
San Fran
London Expo
Inside each event holds all of videos in either avi format or .mkv. I want them to be converted to the same file name. but with the mp4 extension.
My question is how do I loop through a folders sub folders, and also keep the file names because at the moment they have spaces in them.. Here is what I have at the moment.
sourcedir="$1"
destdir="$2"
cd "$sourcedir"
for i in `ls`; do
HandBrakeCLI -i "$i" -o "$destdir/${i%.*}.mp4" --preset="AppleTV"
echo $i
done
Phillips Code:
cd "$sourcedir"
echo "Directory: $sourcedir"
destdir = sourcedir
for subdir in *
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Use a nested loop, and don't use ls:
for subdir in "$sourcedir"/*
do
if [[ -d "$subdir" ]]
then
for file in "$subdir"/*
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
echo "$file"
done
fi
done
Another option is to use find:
find "$sourcedir" -maxdepth 2 -mindepth 2 -type f -exec bash -c 'HandBrakeCLI -i "$0" -o "'"$destdir"'/${0%.*}.mp4" --preset=AppleTV' '{}' ';' -print
Both solutions will work with filenames containing spaces or newlines.
Either use for i in "$sourcedir/*" (or since you've already done a cd there you could do for i in *).
or do find "$sourcedir" -type f | while read -r i (with this, the variable i will include the source directory name, so you'll have to strip that off using a brace expansion or basename).
change program to iVI link to iVi.
this program does the trcik, even better than handbrake...
bash 4
shopt -s globstar
destdir="/somewhere"
for file in **/*.mkv **/*.avi
do
HandBrakeCLI -i "$file" -o "$destdir/${file%.*}.mp4" --preset=AppleTV
done

Resources