Skip to content

Instantly share code, notes, and snippets.

@jlnbxn
Forked from bdurrow/merge-mp4.sh
Last active November 7, 2021 15:08
Show Gist options
  • Save jlnbxn/edd16f23a521bed42d9ac1ae461f312b to your computer and use it in GitHub Desktop.
Save jlnbxn/edd16f23a521bed42d9ac1ae461f312b to your computer and use it in GitHub Desktop.
Bash script to merge all mp4 videos in current directory (recursively 2 levels). It also updates the chapter marks to retain the folder/filename of source dir
#!/bin/bash
#http://redsymbol.net/articles/unofficial-bash-strict-mode/
set -euo pipefail
IFS=$'\n\t'
## Script to merge all mp4 videos in current directory (recursively 2 levels)
## And update chapter marks to retain the folder/filename
## Script for merging videos
temp_dir=$(mktemp -d)
function finish {
rc=$?
if [[ $rc != 0 ]]; then
echo
echo "FAILED!"
fi
echo -n "Cleaning up "
rm -rf "${temp_dir}"
echo "..........[ DONE ]"
exit $rc
}
trap finish EXIT
current_dir=$(pwd)
bname=$(basename ${current_dir})
final_mp4=${bname}.mp4
input_list=${temp_dir}/${bname}-input_list.txt
file_list=${temp_dir}/${bname}-file_list
meta_file=${temp_dir}/${bname}-metadata.txt
#Hopefully this will work for either BSD or GNU sed
extended_match="-r"
echo "" | sed ${extended_match} 's|foo|bar|' 2>/dev/null || extended_match="-E"
if [ -e "${final_mp4}" ]; then
echo "${final_mp4} already exists, please remove it."
exit 1
fi
echo -n "Generating file lists "
find . -maxdepth 2 -type f -iname '*.mp4' | sort -V |
sed -e "s|^./||" |
tee "${file_list}" |
sed -e "s;';'\\\'';g" | # Escape single quotes in filenames
awk "{printf \"file '${current_dir}/%s'\n\", \$0}" > "${input_list}"
echo "..........[ DONE ]"
cat -n "${file_list}"
## chapter marks
#Do this first so we fail early
#TODO: Test (‘=’, ‘;’, ‘#’, ‘\’) are escaped
ts=0
echo -n "Generating chapter marks "
echo -n ""
ffmpeg -i "$(head -1 "${file_list}")" -f ffmetadata "${meta_file}" -v quiet
cat "${file_list}" | while read file
do
ds=$(ffprobe -v quiet -of csv=p=0 -show_entries format=duration "${file}")
# echo "$ds"
escaped_title=$(echo ${file} | sed ${extended_match} -e 's|([=;#])|\\\1|g' -e 's|.[Mm][Pp]4$||' )
echo "[CHAPTER]" >> "${meta_file}"
echo "TIMEBASE=1/1" >> "${meta_file}"
echo "START=${ts}" >> "${meta_file}"
ts=$(awk "BEGIN {print ${ts}+${ds}; exit}")
echo "END=${ts}" >> "${meta_file}"
echo "TITLE=${escaped_title}" >> "${meta_file}"
done
echo "..........[ DONE ]"
# input_list=$(sed -e "s/file '/'bar'/g" "${input_list}" )
cat -n "${input_list}"
echo -n "Merging the files "
ffmpeg -f concat -safe 0 -i "${input_list}" -i "${meta_file}" -map_metadata 1 -codec copy "${final_mp4}" -v quiet
echo "..........[ DONE ]"
echo "Job Completed."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment