Batch process an entire directory of videos or mp4s from a folder with FFmpeg and Editframe (Updated)

Published on May 28, 2023
Last updated on September 25, 2024

6 min read

If you’ve ever tried to perform any sort of changes to a large video collection en masse, you know it’s no easy task. Batch processing can be an efficient way to automate repetitive changes performed on a group of video files—like merging many individual videos into a single file, for example.

In this tutorial, we’ll walk through the process of programmatically merging an entire directory of MP4 videos into a single file using FFmpeg. Then, we’ll explore how to use the Editframe API to programmatically merge videos from a directory in the cloud. Both methods are incredibly useful in instances where you have hundreds of videos that you need combined into one single, continuous file.

Let’s do this.

Files assets

Here are the sample video files provided by pexels.com(opens in a new tab) that we will use in this tutorial:

file1.mp4

file2.mp4

file3.mp4

Part 1: Using FFmpeg

First, we’ll walk through this workflow using FFmpeg.

Required Tools

  • Sample video and audio files (provided above)
  • FFmpeg(opens in a new tab): (You’ll need to install FFmpeg and set up the appropriate environment variables before beginning this tutorial)

Create a video directory in which you would like the final merged video file to live:

mkdir video

Here is the FFmpeg command that merges all the videos in a directory into a single file:

for filename in  video/*.mp4; do
    ffmpeg -y -i "$filename" -c:a copy -c:v copy -bsf:v h264_mp4toannexb -f mpegts "${filename//.mp4/}.ts"
    echo "file './${filename//.mp4/}.ts'" >> concat.txt
done
ffmpeg -f concat -segment_time_metadata 1  -safe 0 -i concat.txt -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p"  stitched-video.mp4
rm -rf concat.txt video/*.ts

Let’s break down what this is doing.

  • In the loop below, we iterate over each mp4 video file in our folder, and run a command that creates a .ts video file. (TS stands for Video Transport Stream, and these files store video data compressed with standard MPEG-2.). Then, we specify a file path for the .ts files in the concat.txt file.
for filename in  video/*.mp4; do
    ffmpeg -y -i "$filename" -c:a copy -c:v copy -bsf:v h264_mp4toannexb -f mpegts "${filename//.mp4/}.ts"
    echo "file './${filename//.mp4/}.ts'" >> concat.txt
done
  • In this line, we concat all of the .ts videos files using the concat.txt file. Additionally, we resize the file to 1920:1080 at a frame rate of 30 fps.
ffmpeg -f concat -segment_time_metadata 1  -safe 0 -i concat.txt -vf "scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:-1:-1,setsar=1,fps=30,format=yuv420p"  stitched-video.mp4
  • Here, after merging the video files together, we remove all of the .ts files we created previously. (This is optional):
rm -rf  video/*.ts
  • In this line, we remove the concat.txt file that holds the video path for the .ts files (this is also optional):
rm -rf concat.txt

Here’s the final output video from the FFmpeg command

stitched-video.mp4

Part 2: Using Editframe

Now let’s perform the same task using Editframe instead of FFmpeg.

Required tools:

  • Node.js installed on your machine
  • No need to have FFmpeg installed on your machine
  • Editframe API Token (you can create an account from this link(opens in a new tab))
  • No need to have an .srt file for the subtitles (you can add subtitles directly to the video using the Editframe elements)

Let’s get started:

  1. Setup a new Node.js project using the Editframe CLI:
npx @editframe/create@beta
  1. Install the required dependencies:
cd batch-process-videos && npm install
  1. Create a new directory called videos and add the video files to the project:
mkdir videos
  1. Add assets to the project:

Add file1.mp4, file2.mp4, and file3.mp4 files to the videos folder.

  1. Update the index.html file with the following code:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <script type="module" src="./src/index.js"></script>
    <link rel="stylesheet" href="./src/styles.css" />
  </head>
  <body>
    <ef-timegroup mode="sequence" class="w-[1920px] h-[1080px] overflow-hidden">
      {{content}}
    </ef-timegroup>
  </body>
</html>

Let’s walk through what the code in this file is doing.

  • In these lines, we import the index.js file and the styles.css file which contains the styles for the video and @editframe/elements:
<head>
    <meta charset="UTF-8" />
    <script type="module" src="./src/index.js"></script>
    <link rel="stylesheet" href="./src/styles.css" />
  </head>
  • In this line, we create a new video composition using the ef-timegroup element.
<ef-timegroup
      mode="sequence"
      class="w-[1920px] h-[1080px" 
    >
    {{content}}
</ef-timegroup>
  1. Create process_videos.js file in the root directory and add the following code:
import fs from 'fs/promises';
import path from 'path';

const sourceFolder = './videos'; 
const destFolder = './src/assets';
const indexFile = './index.html';

async function processVideos() {
  try {
    // Create destination folder if it doesn't exist
    await fs.mkdir(destFolder, { recursive: true });

    // Read the list of video files
    const files = await fs.readdir(sourceFolder);
    const videoFiles = files.filter(file => path.extname(file).toLowerCase() === '.mp4');

    // Copy video files to src/assets
    for (const file of videoFiles) {
      const sourcePath = path.join(sourceFolder, file);
      const destPath = path.join(destFolder, file);
      await fs.copyFile(sourcePath, destPath);
      console.log(`Copied ${file} to ${destFolder}`);
    }

    // Read the index.html file
    let indexContent = await fs.readFile(indexFile, 'utf-8');

    // Generate new video elements
      const videoElements = videoFiles.map(file => `
        <ef-timegroup mode="contain">
        <ef-video src="/assets/${file}"></ef-video>
      </ef-timegroup>
      `
    ).join('');

    // Replace the placeholder in index.html
    indexContent = indexContent.replace(
      '{{content}}',
      videoElements
    );

    // Write the updated content back to index.html
    await fs.writeFile(indexFile, indexContent, 'utf-8');
    console.log('Updated index.html with new video elements');

  } catch (error) {
    console.error('An error occurred:', error);
  }
}

processVideos();
  • In this script, we read the list of video files from the videos folder, copy them to the src/assets folder, and generate new video elements for each video file in the index.html file.
  1. Run the script:
node process_videos.js

This will copy the videos files form videos from videos folder to src/assets folder and update the index.html file with the new video elements.

You should see the following output:

Copied file1.mp4 to ./src/assets
Copied file2.mp4 to ./src/assets
Copied file3.mp4 to ./src/assets
Updated index.html with new video elements

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <script type="module" src="./src/index.js"></script>
    <link rel="stylesheet" href="./src/styles.css" />
  </head>
  <body>
    <ef-timegroup mode="sequence" class="w-[1920px] h-[1080px] overflow-hidden">
      
        <ef-timegroup mode="contain">
        <ef-video src="/assets/file1.mp4"></ef-video>
      </ef-timegroup>
      
        <ef-timegroup mode="contain">
        <ef-video src="/assets/file2.mp4"></ef-video>
      </ef-timegroup>
      
        <ef-timegroup mode="contain">
        <ef-video src="/assets/file3.mp4"></ef-video>
      </ef-timegroup>
      
    </ef-timegroup>
  </body>
</html>
  1. Preview the project:
npx vite .
  1. Update the .env file with your Editframe API token:
EF_TOKEN="YOUR_API_TOKEN"
  1. Render the video:
npx @editframe/cli render .

Here is the final output video from the Editframe command:

editframe-stitched-video.mp4

Note: You can add transitions and filters, resize videos, trim videos, and much more using Editframe. Check out the docs here.

Comparison video between FFmpeg and Editframe API

Here’s a comparison between the videos we created with FFmpeg (left) and Editframe (right):

compare.mp4