0

I am playing around with embedding captions into mp4 video files, and want to find a way to do this across large sets of directories with the .mp4 and .srt files in them. Each pair of .mp4 and .srt files will be subfoldered together in their own directory, and the basename should be the same between the two. Example:

Video1
  Video1.mp4
  Video1.srt
Video2
  Video2.mp4
  Video2.srt

I’ve tried several things but I’m a novice at this and only write very simple bash scripts for much more straightforward processes. For this I need to figure out how to write the bash script to run an ffmpeg command in every subfolder that will grab the mp4 and srt file and output a new mp4 of the merged data. The basic ffmpeg command to do this is:

ffmpeg -i filename.mp4 -i filename.srt -c copy -c:s mov_text output.mp4

I’ve tried to add:

for dir in ./*/; do ffmpeg -i *.mp4 -i *.srt -c copy -c:s move_text “$file”.mp4

…and several variations of this, but ffmpeg always stops with a “*.mp4: No such file or directory” error. Then I tried to add "for file in..." after the "for dir in" statement but didn't have any positive results. The following is closest to what I need - it at least goes to each folder and processes the files - but it does them independently and doesn't combine the mp4 and srt source files as the ffmpeg command should. It outputs a video.mp4.mp4 and video.srt.mp4, and fails to combine them in either case.

for dir in ./**/*;
do ffmpeg -i "$dir" -i "$dir" -c copy -c:s mov_text "$dir".mp4

I tried "$dir".mp4 and "$dir".srt but that just results in an error. I tried to pull just directory names:

for dir in ./**/*;
do ffmpeg -i "$(basename $dir)" -i "$(basename $dir)" -c copy -c:s mov_text "$dir".mp4

and my attempts using "$(basename $dir).extension" have resulted in errors - it looks for video.mp4.mp4 or video.srt.mp4. Any tips as to what to add to get this process to work or another approach entirely would be greatly appreciated! I figure it's a simple bash thing I'm just ignorant of, but certainly need to learn how to do! Thanks!

2 Answers 2

3

Run this within the dir containing Video1/, Video2/...

#!/bin/bash -e
shopt -s globstar
for v in ./**/*.mp4; do
    s=${v%.*}.srt
    if [ -f "$s" ]; then
        ffmpeg -i "$v" -i "$s" -c copy -c:s mov_text "${v##*/}"
    fi
done
  • ./**/*.mp4 expands to ./Video1/Video1.mp4 ./Video1/Video2.mp4 ...,
  • ${v%.*} removes the extension (./Video1/Video1.mp4 > ./Video1/Video1),
  • [ -f "$s" ] checks if $s (i.e. ./Video1/Video1.srt) exists,
  • ${v##*/} extracts the basename of $v (./Video1/Video1.mp4 > Video1.mp4).

So the final structure of . will be like:

Video1.mp4    # subbed
Video1
  Video1.mp4
  Video1.srt
Video2.mp4    # subbed
Video2
  Video2.mp4
  Video2.srt
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for the response oguzismail. I ran this script but it didn't work. I don't get any error messages though. As a quick test, I swapped out the ffmpeg line with a simple echo to a log file, and it didn't do anything to the log file, so it seems the If statement isn't seeing the .srt file.
You'll need to add the line shopt -s globstar before ** will work; it's a feature that's turned off by default. (@oguzismail, I'm assuming your local distro turns that feature on in your interactive shells).
(btw, personally, I advise against reflexive use of -e; see the exercises in BashFAQ #105 to understand why it can be reasonably argued to create as many bugs as it prevents).
Yes I didn't know that, thanks @CharlesDuffy. I think it's ok to use -e in this particular case.
@Katman, ...btw, consider bash -x scriptname for testing -- that'll create a log of every line run during execution, which makes it a lot easier to confirm diagnoses. :)
|
1

As a tweak to the excellent answer by ogizismail, the below is an approach that works with versions of bash too old to support globstar:

while IFS= read -r -d '' v; do
  s=${v%.mp4}.srt
  [[ -e $s ]] && ffmpeg -i "$v" -i "$s" -c copy -c:s mov_text "${v##*/}"
done < <(find . -mindepth 2 -name '*.mp4' -printf '%P\0')

The general technique is discussed in Using Find. Using -mindepth 2 stops it from finding your already-subbed output files.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.