ffmpeg batch script for video resizing I have a few hundred mp4 files (thanks android =/) that I need to convert. I only want to convert videos that are too large (>= 1080p) and I do want to keep the exif Information so the timeline is not broken.

I hope someone can look over the script to point me to some grave mistakes or give some improvement.

Anything I am missing? Thanks in advance.

resize() {
    echo "Filename $1"
    filename=$(basename -- "$1")
    extension="${filename##*.}"
    filename="${filename%.*}"
    new_filename=${filename}.${timestamp}.${extension}
    ffmpeg -v quiet -stats -i $1 -map_metadata 0 \ 
    -vf scale=-1:720 -c:v libx264 -crf 23 \
    -c:a copy $new_filename &lt; /dev/null
    exiftool -TagsFromFile $1 "-all:all&gt;all:all" -overwrite_original     $new_filename
}

if [[ -d $1 ]]; then
    timestamp=$(date +%s)
    echo "Finding Video Files ..."
    exiftool $1/*.mp4 -if '$ImageHeight &gt;= 1080' -p '$Filename' &gt; /tmp/fl_$timestamp

    echo "Processing Video Files ..."
    while IFS= read -r line; do
        resize $line
    done &lt; /tmp/fl_$timestamp
    rm /tmp/fl_$timestamp
elif [[ -f $1 ]]; then
    resize $1
else
    echo "Argument missing"
    exit 1
fi
Consider portable shell
The only Bash feature we're using is [[ ]]  for testing file properties.  It's easy to replace [[ -d $1 ]] with [ -d &quot;$1&quot; ] and that allows us to stick with standard shell, which is more portable and lower overhead:
#!/bin/sh

Careful with quoting
Most of shellcheck's output is due to failure to quote parameter expansions:
236052.sh:9:31: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:9:50: error: Delete trailing spaces after \ to break line (or use quotes for literal space). [SC1101]
236052.sh:10:5: warning: This flag is used as a command name. Bad line break or missing [ .. ]? [SC2215]
236052.sh:11:15: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:12:28: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:12:74: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:18:14: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:18:27: note: Expressions don't expand in single quotes, use double quotes for that. [SC2016]
236052.sh:18:53: note: Expressions don't expand in single quotes, use double quotes for that. [SC2016]
236052.sh:18:75: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:22:16: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:23:20: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:24:16: note: Double quote to prevent globbing and word splitting. [SC2086]
236052.sh:26:12: note: Double quote to prevent globbing and word splitting. [SC2086]

Ironically, you do have quotes in some places they aren't strictly necessary, so it's unclear why you missed all these.
Errors go to standard output

echo &quot;Argument missing&quot;
exit 1


That should be:

echo &gt;&amp;2 &quot;Argument missing&quot;
exit 1


The test here is slightly wrong: the argument may be present, but not the name of a plain file or directory.  So I'd replace that with:
elif [ -e &quot;$1&quot; ]
    echo &quot;$1: not a plain file or directory&quot; &gt;&amp;2
    exit 1
elif [ &quot;$1&quot; ]
    echo &quot;$1: file not found&quot; &gt;&amp;2
    exit 1
else
    echo &quot;Argument missing&quot; &gt;&amp;2
    exit 1
fi

It may be worthwhile to move that testing into the resize function, because at present we assume that the contents found in directory arguments are plain files (that said, we're covering a tiny corner case with that, so I wouldn't sweat it - just let the commands there fail).
Don't assume previous commands succeeded
In resize, if ffmpeg fails, there's little point running exiftool, so connect them with &amp;&amp;.  Also consider removing the file if it was created with errors (so we're not fooled by a partly-written output into thinking this file doesn't need conversion).
Avoid temporary files
There's no need for the file /tmp/fl_$timestamp: we could simply use a pipeline there.
Consider accepting more arguments
Instead of only allowing a single argument (and ignoring all but the first), let the user specify as many files as needed; it's easy to loop over them using for.
Handle directories using recursion
Instead of the while loop, we could invoke our script recursively using xargs.  I'll make it a separate function for clarity:
resize_dir() {
    exiftool &quot;$1&quot;/*.mp4 -if '$ImageHeight &gt;= 1080' -p '$Filename' |
        xargs -r -d '\n' -- &quot;$0&quot; || status=false
}

(xargs -r is a GNU extension to avoid running the command with no arguments.  If this option isn't available, we'll need to modify the script so that passing no arguments isn't an error.)

Modified code
This is Shellcheck-clean, but I'm not able to test it (lacking the requisite directory of MPEG files).
#!/bin/sh

set -eu


status=true
fail() {
    echo &quot;$@&quot; &gt;&amp;2
    status=false
}

# Resize a single file
resize() {
    echo &quot;Filename $1&quot;
    filename=$(basename -- &quot;$1&quot;)
    extension=${filename##*.}
    filename=${filename%.*}
    new_filename=${filename}.${timestamp}.${extension}
    if 
        ffmpeg -v quiet -stats -i &quot;$1&quot; -map_metadata 0 \
               -vf scale=-1:720 -c:v libx264 -crf 23 \
               -c:a copy &quot;$new_filename&quot; &lt; /dev/null &amp;&amp;
            exiftool -TagsFromFile &quot;$1&quot; '-all:all&gt;all:all' \
                     -overwrite_original &quot;$new_filename&quot;
    then
        # success
        true
    else
        # failed; destroy the evidence
        rm -f &quot;$new_filename&quot; 2&gt;/dev/null
        fail &quot;Failed to convert $1&quot;
    fi
}

# Resize all *.mp4 files in a single directory
# N.B. only immediate contents; not recursive
resize_dir() {
    # shellcheck disable=SC2016
    exiftool &quot;$1&quot;/*.mp4 -if '$ImageHeight &gt;= 1080' -p '$Filename' |
        xargs -r -d '\n' -- &quot;$0&quot; || status=false
}

[ $# -gt 0 ] || fail &quot;Usage: $0 FILE FILE...&quot;

timestamp=$(date +%s)

for arg
do
    if [ -d &quot;$arg&quot; ]
    then
        resize_dir &quot;$arg&quot;
    elif [ -f &quot;$arg&quot; ]
    then
        resize &quot;$arg&quot;
    elif [ -e &quot;$arg&quot; ]
    then
        fail &quot;$arg: not a plain file or directory&quot;
    else
        fail &quot;$arg: file not found&quot;
    fi
done

exec $status # true or false