Skip files in loop which already have FINAL in their name - bash

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.

Related

How to loop through files in subdirectories?

I'm trying to convert a bunch of .aac files to .mp3 files in separate subdirectories in a directory. This is the code I've written:
for dir in */
do
OUTPUT_DIR=./mp3
mkdir "$OUTPUT_DIR"
for i in *.aac;
do name=`echo "$i" | cut -d'.' -f1`
echo "$name"
ffmpeg -i "$i" -vn -ar 44100 -ac 2 -b:a 192k "$OUTPUT_DIR/${name}.mp3"
done
done
However this just try to run the commands in the outside directory and gives an error. I'm new at bash so it might be a very simple mistake I'm not seeing.
How to loop through files in subdirectories?
Read ex How to loop through a directory recursively to delete files with certain extensions
This is the code I've written:
Check your scripts with http://shellcheck.net
Do not use `. Use $(...) instead.
To get file name and dir see basename and dirname commands. Your name=echo "$i" | cut -d'.' -f1 will fail when directory has a dot.
Use printf "%s\n" "$i" or in bash <<<"$i" here string instead echo "$i" |. See ex. https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo .
Prefer lower case variables in your scripts.
so it might be a very simple mistake I'm not seeing.
You are not changing the direcotry at all. You need to cd somewhere to that dir and then cd - back (or pushd + popd). But just reference directories relative to your current dir anyway. Maybe something along:
for dir in ./*/; do
# create the directory in dir
output_dir="./$dir/mp3"
mkdir "$output_dir"
# find files aac inside $dir/
for i in "$dir"/*.aac; do
name=$(basename "$i" .acc)
echo "$name"
ffmpeg -i "$i" -vn -ar 44100 -ac 2 -b:a 192k "$output_dir/${name}.mp3"
done
done
You may want to use find to recursively find all files in all subdirectories of a directory.
Here's an example (it assumes you also want to recreate the subdirectories in the output directory).
OUTPUT_DIR=./mp3
for item in $(find . -type f -name *.aac); do
name="$(basename $item .aac)"
subdir="$(dirname $item)"
mkdir -p "$OUTPUT_DIR/$subdir"
ffmpeg -i "$name" -vn -ar 44100 -ac 2 -b:a 192k "$OUTPUT_DIR/$subdir/$name.mp3"
done

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

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