Last active
August 29, 2024 23:43
-
-
Save kowalcj0/ae0bdc43018e2718fb75290079b8839a to your computer and use it in GitHub Desktop.
Extract all subtitles from a movie using ffprobe & ffmpeg
This file contains hidden or 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
| alias subs=subs | |
| function subs() { | |
| movie="${1}" | |
| filename="${1%.*}" | |
| mappings=`ffprobe -loglevel error -select_streams s -show_entries stream=index:stream_tags=language -of csv=p=0 "${movie}"` | |
| OLDIFS=$IFS | |
| IFS=, | |
| ( while read idx lang | |
| do | |
| echo "Exctracting ${lang} subtitle #${idx} from ${movie}" | |
| ffmpeg -nostdin -hide_banner -loglevel quiet -i "${movie}" -map 0:"$idx" "${filename}_${lang}_${idx}.srt" | |
| done <<< "${mappings}" ) | |
| IFS=$OLDIFS | |
| } |
The issue im having is this producing 0kb sub files.
#!/usr/bin/env bash
function subs() {
local movie idx lang subs=
local -a dests=()
for movie
do
ffprobe -loglevel error -select_streams s -show_entries stream=index:stream_tags=language -of csv=p=0 "$movie" |
{
while IFS=, read idx lang
do
subs+=" ${lang}_$idx"
dests+=(-map "0:$idx" "${movie%.*}_${lang}_$idx.srt")
done
if test -n "$subs"
then
echo "Extracting subtitles from $movie:$subs"
ffmpeg -nostdin -y -hide_banner -loglevel quiet -i "$movie" "${dests[@]}"
else
echo "No subtitles in $movie"
fi
}
done
}
subs "${1}"
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment

If an input file has more than one embedded subtitle, you can save a lot of time by building a command line that makes a single invocation of
ffmpegextract all of them in one pass.Rather than have the function process only its first argument, there's an outer
forloop that walks through all of them so you can pass multiple filenames explicitly and/or use wildcards.The list of available subtitle streams is piped straight from the output of
ffprobeinto thewhile readloop that parses them, rather than being collected into a shell variable in between. This requires that thesubsanddests[]variables that the loop fills in are then used within the same brace-delimited compound statement as the loop itself: the shell runs pipeline components in parallel, each in its own subshell. If it were only the actualwhileloop that had theffprobeoutput piped into it, likethen the loop's subshell would terminate with the loop, and the changes made to
subsanddests[]inside the loop would be lost. Note also the use of environment variable prefix syntax to supply a modifiedIFSto a single command only (thereadbuiltin, in this instance), making it clearer whyIFSis being modified and removing the need for an explicit save and restore.Building
dests[]as an array with each of what will becomeffmpegarguments as an individual element, rather than simply appending pieces to a single string as is done withsubs, allows those arguments to be cleanly expanded into theffmpegcommand line as separate words even if they contain whitespace or special shell characters. Trying to do this kind of thing in a strictly POSIX-compatible shell that doesn't support arrays is always a nightmare of horrible edge cases. Just don't.