Created
May 31, 2021 22:04
-
-
Save jfharden/aa34fbd0a2fba67f11e501633afcfd68 to your computer and use it in GitHub Desktop.
Script to convert m4v to mkv files and maintain audio/subtitle stream names
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Requirements to use this script: | |
# HandBrakeCLI | |
# jq | |
# bash 4+ (sorry osx!) | |
# ffmpeg | |
set -e | |
# Default to 1 if DRY_RUN is either unset or empty | |
DRY_RUN=${DRY_RUN:-1} | |
INPUT_FILE="$1" | |
function usage { | |
echo "Usage:" | |
echo " $0 <input_file>" | |
echo | |
echo "Note: Environment variable DRY_RUN must be set to 0 in order to actually run the conversion." | |
echo "Not setting DRY_RUN to 0 will cause the command line will just to be printed and not executed" | |
echo | |
echo "Example:" | |
echo " DRY_RUN=0 $0 ./my_file.m4v" | |
echo | |
exit 1 | |
} | |
if [ $# -ne 1 ]; then | |
echo "Error: Wrong number of arguments" | |
echo | |
usage | |
fi | |
if [[ ! "$INPUT_FILE" =~ \.m4v$ ]]; then | |
echo "Error: Not an m4v file, please specify an m4v file as the input file" | |
echo | |
usage | |
fi | |
if [ ! -f "$INPUT_FILE" ]; then | |
echo "Error: Input file '$INPUT_FILE' does not exist, or is not a regular file" | |
echo | |
usage | |
fi | |
OUTPUT_FILE=$(sed -E 's/.m4v$/.mkv/' <<<"$INPUT_FILE") | |
echo " Input File: $INPUT_FILE" | |
echo "Output File: $OUTPUT_FILE" | |
# The json output from HandbrakeCLI isn't valid json, it's got 2 json blobs each prefixed with some text, the 'JSON | |
# Title Set' is the one we care about and is the second so we'll use a perl multi-line regex to remove everything up to | |
# the json blob we care about | |
INPUT_STREAMS=$(HandBrakeCLI --scan --json --input "$INPUT_FILE" 2>/dev/null | perl -0777 -pe 's/.*JSON Title Set: //gs') | |
FFMPEG_FLAGS=( | |
-i "$INPUT_FILE" # Input filename | |
-codec copy # copy streams without transcoding | |
-map 0:v # copy all video streams in the same order | |
-map 0:a # copy all audio streams in the same order | |
-map 0:s # copy all subtitle streams in the same order | |
) | |
# Whan changing track metadata the arguments are constructed as follows: | |
# -meteadata:<s for stream>:<a for audio,s for subtitle>:<stream_index (0 based)> title="Stream Title" | |
# | |
# Examples: | |
# -metadata:s:a:0 title="Audio Stream Title 1" | |
# -metadata:s:s:0 title="Subtitle Stream Title 1" | |
# -metadata:s:a:1 title="Audio Stream Title 2" | |
# -metadata:s:s:1 title="Subtitle Stream Title 2" | |
readarray -t AUDIO_NAMES < <( | |
jq -r '[.TitleList[0].AudioList[].Name] | map(if . == null then . = "" else . end) | .[]' <<< "$INPUT_STREAMS" | |
) | |
for STREAM_INDEX in $(seq 0 $((${#AUDIO_NAMES[@]} - 1))); do | |
STREAM_NAME=${AUDIO_NAMES[$STREAM_INDEX]} | |
if [ -n "$STREAM_NAME" ]; then | |
FFMPEG_FLAGS+=( | |
"-metadata:s:a:$STREAM_INDEX" "title=$STREAM_NAME" | |
) | |
fi | |
done | |
readarray -t SUBTITLE_NAMES < <( | |
jq -r '[.TitleList[0].SubtitleList[].Name] | map(if . == null then . = "" else . end) | .[]' <<< "$INPUT_STREAMS" | |
) | |
for STREAM_INDEX in $(seq 0 $((${#SUBTITLE_NAMES[@]} - 1))); do | |
STREAM_NAME=${SUBTITLE_NAMES[$STREAM_INDEX]} | |
if [ -n "$STREAM_NAME" ]; then | |
FFMPEG_FLAGS+=( | |
"-metadata:s:s:$STREAM_INDEX" "title=$STREAM_NAME" | |
) | |
fi | |
done | |
# # Turns out with HandBrakeCLI it's been setting the default flag on the first sub track even when I've had | |
# --subtitle-default none so I can't rely on the default flag to tell me a track should be forced. | |
# DEFAULT_SUBTITLE_TRACK=$( | |
# jq -r '[.TitleList[0].SubtitleList[]] | map(.Attributes.Default) | index(true) | values' <<< "$INPUT_STREAMS" | |
# ) | |
# if [ -n "$DEFAULT_SUBTITLE_TRACK" ]; then | |
# # Plex ignores default flags, and when HandBrakeCli sets a default in mkv it actually sets the forced flag as well as | |
# # the default, but ffmpeg correctly copies the default when we convert so we just need to apply the forced flag | |
# # to the default subtitle | |
# FFMPEG_FLAGS+=( | |
# "-disposition:s:$DEFAULT_SUBTITLE_TRACK" "forced" | |
# ) | |
# fi | |
FFMPEG_FLAGS+=( | |
"$OUTPUT_FILE" | |
) | |
echo ffmpeg "${FFMPEG_FLAGS[@]}" | |
if [ "$DRY_RUN" -eq 0 ]; then | |
ffmpeg "${FFMPEG_FLAGS[@]}" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment