Skip to content

Instantly share code, notes, and snippets.

@heikomat
Created December 30, 2020 11:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heikomat/8b26b825679693562d7cbdcbdc41549d to your computer and use it in GitHub Desktop.
Save heikomat/8b26b825679693562d7cbdcbdc41549d to your computer and use it in GitHub Desktop.
#!/bin/bash
ROOT_FOLDER="${1}"
SOURCE_FOLDER="${ROOT_FOLDER}/${2}"
CACHE_FOLDER="${ROOT_FOLDER}/${3}"
TARGET_FOLDER="${ROOT_FOLDER}/${4}"
TARGET_BITRATE="${5}"
# abort, if there is a lock file
LOCKFILE="${ROOT_FOLDER}/converting.lock"
if [ -f "${LOCKFILE}" ]; then
LOCKFILE="${ROOT_FOLDER}/converting2.lock"
fi
if [ -f "${LOCKFILE}" ]; then
LOCKFILE="${ROOT_FOLDER}/converting3.lock"
fi
if [ -f "${LOCKFILE}" ]; then
exit 0;
fi
if [ -z "${TARGET_BITRATE}" ]; then
TARGET_BITRATE="4000"
fi
TARGET_BUFSIZE=$(bc -l <<< "${TARGET_BITRATE} * 2")
function findFirstUnconvertedMovie {
# find all mkv, mp4 and avi files in SOURCE_FOLDER and its subfolders
for file in ${SOURCE_FOLDER}/{**/*,**/**/*,*}.{mkv,mp4,avi,m2ts}; do
[ -e "$file" ] || continue # if glob doesn't match, continue
relative_file_path=$(realpath --no-symlinks --relative-to="${SOURCE_FOLDER}" "${file}")
[ -f "${CACHE_FOLDER}/${relative_file_path}.txt" ] && continue # if the file has been converted before, continue
echo "$relative_file_path"
return 0
done
}
function convertMovie {
target_folder=$(dirname "${CACHE_FOLDER}/${1}")
mkdir --parents "${target_folder}"
base_filename=${1%.*}
color_space=$(mediainfo "${SOURCE_FOLDER}/${1}" --Inform="Video;%colour_primaries%")
video_filter="zscale=width='min(1920,iw)':height=-2:filter=lanczos"
if [ "$color_space" = "BT.2020" ]; then
# this is an HDR video, apply tone mapping
video_filter="${video_filter},zscale=transfer=linear:npl=140,tonemap=tonemap=hable:desat=5.0,zscale=primaries=bt709:transfer=bt709:matrix=bt709:range=limited:dither=ordered,format=yuv420p"
fi
nice -19 \
ffmpeg -probesize 100000000 `# prevent the probing of the video info from failing by allowing a larger portion of it to be probed` \
-analyzeduration 100000000 `# prevent the analyzing of the video info from failing by allowing a larger portion of it to be analyzed` \
-i "${SOURCE_FOLDER}/${1}" \
-map 0:V:0 `# take first non-picture video stream` \
-map 0:a `# take all audio streams` \
-map 0:d? `# take all data streams` \
-map 0:s? `# take all subtitle streams` \
-crf 17 \
-c:a copy \
-c:s copy \
-vf "${video_filter}" `# apply 1080p-scaling and possibly colorspace mapping` \
-threads 12 \
-preset veryslow `# get the best possible quality out of the available bitrate` \
-profile:v high `# "high10" is not supported everywhere, "main" and "baseline" don't allow for 1080p, so "high" it is` \
-level:v 4.0 `# older devices don't support levels > 4.1, see https://support.plex.tv/articles/204377253-what-media-formats-are-supported/` \
-pix_fmt yuv420p `# force color depths of 8-bit` \
-movflags faststart `# move metadata of the video to the start for faster playback starting` \
-max_muxing_queue_size 4000 `# prevent "Too many packets buffered for output"` \
-maxrate "${TARGET_BITRATE}"k \
-bufsize "${TARGET_BUFSIZE%.*}"k \
"${CACHE_FOLDER}/${base_filename}.mkv"\
2> "${CACHE_FOLDER}/${1}.txt"
}
target_movie=$(findFirstUnconvertedMovie)
echo "$target_movie"
if [ -z "${target_movie}" ]; then
# There is no more movie that needs converting, lift the lock and exit
exit 0
fi
# create the lock file, so no one else tries converting while we are
touch "${LOCKFILE}"
convertMovie "$target_movie"
# copy the converted movie to the destination folder
target_folder=$(dirname "${TARGET_FOLDER}/${target_movie}")
target_filename="${target_movie%.*}.mkv"
mkdir --parents "${target_folder}"
mv --force "${CACHE_FOLDER}/${target_filename}" "${TARGET_FOLDER}/${target_filename}"
# lift the lock
rm "${LOCKFILE}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment