Hi,
I was fiddling around with ffmpeg video conversion because my storage capacity for movies it nearly full.
So I’ve put together a bash script that will find every file in the current directory and every subdirectory (except some subtitles for examle) and then pass them to ffmpeg with some default values - nothing too fancy. Output movie file is stored in a new directory _converted
with an optional ffmpeg
log file.
Use -h
argument for help.
Perhaps someone will find it useful as well.
The script - click here
#!/bin/bash
dir=$(pwd)
# default storage locations
out_dir="_converted"
out_log="${out_dir}/_log"
# error output storage
err_file="_00000_ERROR_conversion.log"
rm -f "${dir}/${err_file}"
# default values
height=480 # maximal pixel height of converted video
crf=24 # video quality
aa='-map 0:a? -c:a copy' # audio mapping
ss='-map 0:s? -c:s copy' # subtitles mapping
log=1 # default true
# print help
usage() {
echo "Usage: $0 [-hf:t:P:q:sal]
-h ........... \"this help\"
-f <val> ..... \"from time - format hh:mm:ss\"
-t <val> ..... \"to time - format hh:mm:ss (make sure f<t)\"
-P <val> ..... \"pixel height (default ${height}p)\"
-q <val> ..... \"video quality as crf (default ${crf}; 0=lossless, 51=worst)\"
-s ........... \"no subtitles\"
-a ........... \"no audio\"
-l ........... \"no conversion logging\"
" 1>&2; exit "$1"; }
# parse command line arguments
while getopts "hf:t:P:q:sal" arg; do
case "${arg}" in
h)
usage 0
;;
f)
f="-ss ${OPTARG}"
;;
t)
t="-to ${OPTARG}"
;;
P)
height="${OPTARG}"
;;
q)
crf="${OPTARG}"
;;
s)
ss=''
;;
a)
aa=''
;;
l)
log=0
;;
*)
usage 1
;;
esac
done
# find all files and get rid of all already converted and some non-video files
# then for each file make the conversion
find "${dir}" -type f |\
grep -Evie "^.*(${out_dir}).*$" |\
grep -Evie '^.*\.(txt|srt|sub|log|i?nfo|rar|zip|7z)$' |\
sort -Vf |\
while read file; do
# create storage directories
mkdir -p "${file%/*}/${out_dir}"
[[ log -eq 1 ]] && mkdir -p "${file%/*}/${out_log}"
# get file name for further processing
printf "${file}"
filename=${file##*/}
# log storage file from the ffmpeg output
if [[ log -eq 1 ]]; then
log_file="${file%/*}/${out_log}/${filename%.*}_${height}p.log"
else
log_file="/dev/null"
fi
# conversion time measurement
TIME_START=$(date +%s%N)
# the conversion itself
ffmpeg -y -i "${file}" \
${f} \
${t} \
-map 0:v? -c:v libx265 -crf ${crf} -vf scale="trunc(oh*a/2)*2:min(${height}\,ih)" \
${aa} \
${ss} \
"${file%/*}/${out_dir}/${filename%.*}_${height}p.mkv" \
2> "${log_file}" < /dev/null
# store which files return with an error code
if [ $? -ne 0 ]; then
echo "${file}" >> "${dir}/${err_file}"
fi
# conversion time measurement
TIME_STOP=$(date +%s%N)
printf "\t\tdone: %.2f s\n" "$(echo "(${TIME_STOP}-${TIME_START})/1000000000" | bc -l | sed -e "s/,/\./g")"
done
# print all files that had a problem with the conversion
if [[ -f "${err_file}" ]]; then
echo -e "\n\nconversion errors:"
cat "${err_file}"
fi