-
-
Save kowalcj0/ae0bdc43018e2718fb75290079b8839a to your computer and use it in GitHub Desktop.
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 | |
} |
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 ffmpeg
extract all of them in one pass.
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
}
Rather than have the function process only its first argument, there's an outer for
loop 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 ffprobe
into the while read
loop that parses them, rather than being collected into a shell variable in between. This requires that the subs
and dests[]
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 actual while
loop that had the ffprobe
output piped into it, like
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
then the loop's subshell would terminate with the loop, and the changes made to subs
and dests[]
inside the loop would be lost. Note also the use of environment variable prefix syntax to supply a modified IFS
to a single command only (the read
builtin, in this instance), making it clearer why IFS
is being modified and removing the need for an explicit save and restore.
Building dests[]
as an array with each of what will become ffmpeg
arguments as an individual element, rather than simply appending pieces to a single string as is done with subs
, allows those arguments to be cleanly expanded into the ffmpeg
command 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.
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}"
Hi @anhthoai
To debug, this issue, I'd suggest to start with printing out the
mappings
before thewhile
loop.Simply, type:
echo ${mappings}
before theOLDIFS=$IFS
line, and re-run the script.If you're going to see something like:
2,eng 3,fre 4,rus
it means that the video file has the language tags correctly set and the index (idx) to language mappings are properly defined.If you're going to see something different, then please post your output.
The other thing, that I'd check is the format of the embedded subtitles.
You can display it with
ffprobe
:ffprobe -loglevel error -select_streams s -show_entries stream=codec_name,index:stream_tags=language -of csv=p=0 "your-video.mkv" 2,subrip,eng 3,subrip,fre 4,subrip,rus
If you're going to see the subtitle format other than
subrip
then you might need to change the extension of the extracted subtitle from${idx}.srt
to:${idx}.ass
forass
(also know asSubStationAlpha
)${idx}.pgs
forhdmv_pgs_subtitle